Graphviz  2.29.20120524.0446
lib/common/utils.c
Go to the documentation of this file.
00001 /* $Id$ $Revision$ */
00002 /* vim:set shiftwidth=4 ts=8: */
00003 
00004 /*************************************************************************
00005  * Copyright (c) 2011 AT&T Intellectual Property 
00006  * All rights reserved. This program and the accompanying materials
00007  * are made available under the terms of the Eclipse Public License v1.0
00008  * which accompanies this distribution, and is available at
00009  * http://www.eclipse.org/legal/epl-v10.html
00010  *
00011  * Contributors: See CVS logs. Details at http://www.graphviz.org/
00012  *************************************************************************/
00013 
00014 #include "render.h"
00015 #include "agxbuf.h"
00016 #include "htmltable.h"
00017 #include "entities.h"
00018 #include "logic.h"
00019 #include "gvc.h"
00020 
00021 #ifdef WIN32
00022 #include "libltdl/lt_system.h"
00023 #endif
00024 #ifndef WIN32
00025 #include <unistd.h>
00026 #endif
00027 #include <ctype.h>
00028 
00029 /*
00030  *  a queue of nodes
00031  */
00032 nodequeue *new_queue(int sz)
00033 {
00034     nodequeue *q = NEW(nodequeue);
00035 
00036     if (sz <= 1)
00037         sz = 2;
00038     q->head = q->tail = q->store = N_NEW(sz, node_t *);
00039     q->limit = q->store + sz;
00040     return q;
00041 }
00042 
00043 void free_queue(nodequeue * q)
00044 {
00045     free(q->store);
00046     free(q);
00047 }
00048 
00049 void enqueue(nodequeue * q, node_t * n)
00050 {
00051     *(q->tail++) = n;
00052     if (q->tail >= q->limit)
00053         q->tail = q->store;
00054 }
00055 
00056 node_t *dequeue(nodequeue * q)
00057 {
00058     node_t *n;
00059     if (q->head == q->tail)
00060         n = NULL;
00061     else {
00062         n = *(q->head++);
00063         if (q->head >= q->limit)
00064             q->head = q->store;
00065     }
00066     return n;
00067 }
00068 
00069 int late_int(void *obj, attrsym_t * attr, int def, int low)
00070 {
00071     char *p;
00072     char *endp;
00073     int rv;
00074     if (attr == NULL)
00075         return def;
00076     p = ag_xget(obj, attr);
00077     if (!p || p[0] == '\0')
00078         return def;
00079     rv = strtol (p, &endp, 10);
00080     if (p == endp) return def;  /* invalid int format */
00081     if (rv < low) return low;
00082     else return rv;
00083 }
00084 
00085 double late_double(void *obj, attrsym_t * attr, double def, double low)
00086 {
00087     char *p;
00088     char *endp;
00089     double rv;
00090 
00091     if (!attr || !obj)
00092         return def;
00093     p = ag_xget(obj, attr);
00094     if (!p || p[0] == '\0')
00095         return def;
00096     rv = strtod (p, &endp);
00097     if (p == endp) return def;  /* invalid double format */
00098     if (rv < low) return low;
00099     else return rv;
00100 }
00101 
00102 char *late_string(void *obj, attrsym_t * attr, char *def)
00103 {
00104     if (!attr || !obj)
00105         return def;
00106 #ifndef WITH_CGRAPH
00107     return agxget(obj, attr->index);
00108 #else /* WITH_CGRAPH */
00109     return agxget(obj, attr);
00110 #endif /* WITH_CGRAPH */
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 || (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 #ifndef WITH_CGRAPH
00126     return mapbool(agxget(obj, attr->index));
00127 #else /* WITH_CGRAPH */
00128 
00129     return mapbool(agxget(obj, attr));
00130 #endif /* WITH_CGRAPH */
00131 }
00132 
00133 /* union-find */
00134 node_t *UF_find(node_t * n)
00135 {
00136     while (ND_UF_parent(n) && (ND_UF_parent(n) != n)) {
00137 #ifndef WITH_CGRAPH
00138         if (ND_UF_parent(n)->u.UF_parent)
00139             ND_UF_parent(n) = ND_UF_parent(n)->u.UF_parent;
00140 #else /* WITH_CGRAPH */
00141         if (ND_UF_parent(ND_UF_parent(n)))
00142             ND_UF_parent(n) = ND_UF_parent(ND_UF_parent(n));
00143 #endif /* WITH_CGRAPH */
00144         n = ND_UF_parent(n);
00145     }
00146     return n;
00147 }
00148 
00149 node_t *UF_union(node_t * u, node_t * v)
00150 {
00151     if (u == v)
00152         return u;
00153     if (ND_UF_parent(u) == NULL) {
00154         ND_UF_parent(u) = u;
00155         ND_UF_size(u) = 1;
00156     } else
00157         u = UF_find(u);
00158     if (ND_UF_parent(v) == NULL) {
00159         ND_UF_parent(v) = v;
00160         ND_UF_size(v) = 1;
00161     } else
00162         v = UF_find(v);
00163 #ifndef WITH_CGRAPH
00164     if (u->id > v->id) {
00165 #else /* WITH_CGRAPH */
00166     if (ND_id(u) > ND_id(v)) {
00167 #endif /* WITH_CGRAPH */
00168         ND_UF_parent(u) = v;
00169         ND_UF_size(v) += ND_UF_size(u);
00170     } else {
00171         ND_UF_parent(v) = u;
00172         ND_UF_size(u) += ND_UF_size(v);
00173         v = u;
00174     }
00175     return v;
00176 }
00177 
00178 void UF_remove(node_t * u, node_t * v)
00179 {
00180     assert(ND_UF_size(u) == 1);
00181     ND_UF_parent(u) = u;
00182     ND_UF_size(v) -= ND_UF_size(u);
00183 }
00184 
00185 void UF_singleton(node_t * u)
00186 {
00187     ND_UF_size(u) = 1;
00188     ND_UF_parent(u) = NULL;
00189     ND_ranktype(u) = NORMAL;
00190 }
00191 
00192 void UF_setname(node_t * u, node_t * v)
00193 {
00194     assert(u == UF_find(u));
00195     ND_UF_parent(u) = v;
00196     ND_UF_size(v) += ND_UF_size(u);
00197 }
00198 
00199 pointf coord(node_t * n)
00200 {
00201     pointf r;
00202 
00203     r.x = POINTS_PER_INCH * ND_pos(n)[0];
00204     r.y = POINTS_PER_INCH * ND_pos(n)[1];
00205     return r;
00206 }
00207 
00208 /* findStopColor:
00209  * Check for colon in colorlist. If one exists, and not the first
00210  * character, store the characters before the colon in clrs[0] and
00211  * the characters after the colon (and before the next or end-of-string)
00212  * in clrs[1]. If there are no characters after the first colon, clrs[1]
00213  * is NULL. Return TRUE.
00214  * If there is no non-trivial string before a first colon, set clrs[0] to
00215  * NULL and return FALSE.
00216  *
00217  * Note that memory is allocated as a single block stored in clrs[0] and
00218  * must be freed by calling function.
00219  */
00220 boolean findStopColor (char* colorlist, char* clrs[2])
00221 {
00222     char* p;
00223     char* s;
00224     int len;
00225 
00226     if ((*colorlist == ':') || !(p = strchr(colorlist,':'))) {
00227         clrs[0] = NULL;
00228         return FALSE;
00229     }
00230 
00231     clrs[0] = N_GNEW (strlen(colorlist)+1,char); 
00232     len = p-colorlist;
00233     memcpy (clrs[0],colorlist,len);
00234     clrs[0][len]= '\0';
00235 
00236     p++;
00237     if ((*p == '\0') || (*p == ':'))
00238         clrs[1] = NULL;
00239     else {
00240         clrs[1] = clrs[0] + (len+1);
00241         if ((s = strchr(p,':'))) {
00242             *s = '\0';
00243             strcpy (clrs[1],p); 
00244             *s = ':';
00245         }
00246         else
00247             strcpy (clrs[1], p);
00248     }
00249     return TRUE;
00250 }
00251 
00252 /* from Glassner's Graphics Gems */
00253 #define W_DEGREE 5
00254 
00255 /*
00256  *  Bezier : 
00257  *      Evaluate a Bezier curve at a particular parameter value
00258  *      Fill in control points for resulting sub-curves if "Left" and
00259  *      "Right" are non-null.
00260  * 
00261  */
00262 pointf Bezier(pointf * V, int degree, double t, pointf * Left, pointf * Right)
00263 {
00264     int i, j;                   /* Index variables      */
00265     pointf Vtemp[W_DEGREE + 1][W_DEGREE + 1];
00266 
00267     /* Copy control points  */
00268     for (j = 0; j <= degree; j++) {
00269         Vtemp[0][j] = V[j];
00270     }
00271 
00272     /* Triangle computation */
00273     for (i = 1; i <= degree; i++) {
00274         for (j = 0; j <= degree - i; j++) {
00275             Vtemp[i][j].x =
00276                 (1.0 - t) * Vtemp[i - 1][j].x + t * Vtemp[i - 1][j + 1].x;
00277             Vtemp[i][j].y =
00278                 (1.0 - t) * Vtemp[i - 1][j].y + t * Vtemp[i - 1][j + 1].y;
00279         }
00280     }
00281 
00282     if (Left != NULL)
00283         for (j = 0; j <= degree; j++)
00284             Left[j] = Vtemp[j][0];
00285     if (Right != NULL)
00286         for (j = 0; j <= degree; j++)
00287             Right[j] = Vtemp[degree - j][j];
00288 
00289     return (Vtemp[degree][0]);
00290 }
00291 
00292 #ifdef DEBUG
00293 edge_t *debug_getedge(graph_t * g, char *s0, char *s1)
00294 {
00295     node_t *n0, *n1;
00296     n0 = agfindnode(g, s0);
00297     n1 = agfindnode(g, s1);
00298     if (n0 && n1)
00299         return agfindedge(g, n0, n1);
00300     else
00301         return NULL;
00302 }
00303 #ifdef WITH_CGRAPH
00304 Agraphinfo_t* GD_info(g) { return ((Agraphinfo_t*)AGDATA(g));}
00305 Agnodeinfo_t* ND_info(n) { return ((Agnodeinfo_t*)AGDATA(n));}
00306 #endif
00307 #endif
00308 
00309 #if !defined(MSWIN32) && !defined(WIN32)
00310 #include        <pwd.h>
00311 
00312 #if 0
00313 static void cleanup(void)
00314 {
00315     agxbfree(&xb);
00316 }
00317 #endif
00318 #endif
00319 
00320 /* Fgets:
00321  * Read a complete line.
00322  * Return pointer to line, 
00323  * or 0 on EOF
00324  */
00325 char *Fgets(FILE * fp)
00326 {
00327     static int bsize = 0;
00328     static char *buf;
00329     char *lp;
00330     int len;
00331 
00332     len = 0;
00333     do {
00334         if (bsize - len < BUFSIZ) {
00335             bsize += BUFSIZ;
00336             buf = grealloc(buf, bsize);
00337         }
00338         lp = fgets(buf + len, bsize - len, fp);
00339         if (lp == 0)
00340             break;
00341         len += strlen(lp);      /* since lp != NULL, len > 0 */
00342     } while (buf[len - 1] != '\n');
00343 
00344     if (len > 0)
00345         return buf;
00346     else
00347         return 0;
00348 }
00349 
00350 /* safefile:
00351  * Check to make sure it is okay to read in files.
00352  * It returns NULL if the filename is trivial.
00353  *
00354  * If the application has set the SERVER_NAME environment variable, 
00355  * this indicates it is web-active. In this case, it requires that the GV_FILE_PATH
00356  * environment variable be set. This gives the legal directories
00357  * from which files may be read. safefile then derives the rightmost component
00358  * of filename, where components are separated by a slash, backslash or colon, 
00359  * It then checks for the existence of a file consisting of a directory from 
00360  * GV_FILE_PATH followed by the rightmost component of filename. It returns the
00361  * first such found, or NULL otherwise.
00362  * The filename returned is thus
00363  * Gvfilepath concatenated with the last component of filename,
00364  * where a component is determined by a slash, backslash or colon
00365  * character.  
00366  *
00367  * If filename contains multiple components, the user is
00368  * warned, once, that everything to the left is ignored.
00369  *
00370  * For non-server applications, we use the path list in Gvimagepath to 
00371  * resolve relative pathnames.
00372  *
00373  * N.B. safefile uses a fixed buffer, so functions using it should use the
00374  * value immediately or make a copy.
00375  */
00376 #ifdef WIN32
00377 #define PATHSEP ";"
00378 #else
00379 #define PATHSEP ":"
00380 #endif
00381 
00382 static char** mkDirlist (const char* list, int* maxdirlen)
00383 {
00384     int cnt = 0;
00385     char* s = strdup (list);
00386     char* dir;
00387     char** dirs = NULL;
00388     int maxlen = 0;
00389 
00390     for (dir = strtok (s, PATHSEP); dir; dir = strtok (NULL, PATHSEP)) {
00391         dirs = ALLOC (cnt+2,dirs,char*);
00392         dirs[cnt++] = dir;
00393         maxlen = MAX(maxlen, strlen (dir));
00394     }
00395     dirs[cnt] = NULL;
00396     *maxdirlen = maxlen;
00397     return dirs;
00398 }
00399 
00400 static char* findPath (char** dirs, int maxdirlen, const char* str)
00401 {
00402     static char *safefilename = NULL;
00403     char** dp;
00404 
00405         /* allocate a buffer that we are sure is big enough
00406          * +1 for null character.
00407          * +1 for directory separator character.
00408          */
00409     safefilename = realloc(safefilename, (maxdirlen + strlen(str) + 2));
00410 
00411     for (dp = dirs; *dp; dp++) {
00412         sprintf (safefilename, "%s%s%s", *dp, DIRSEP, str);
00413         if (access (safefilename, R_OK) == 0)
00414             return safefilename;
00415     }
00416     return NULL;
00417 }
00418 
00419 const char *safefile(const char *filename)
00420 {
00421     static boolean onetime = TRUE;
00422     static char *pathlist = NULL;
00423     static int maxdirlen;
00424     static char** dirs;
00425     const char *str, *p;
00426 
00427     if (!filename || !filename[0])
00428         return NULL;
00429 
00430     if (HTTPServerEnVar) {   /* If used as a server */
00431         /* 
00432          * If we are running in an http server we allow
00433          * files only from the directory specified in
00434          * the GV_FILE_PATH environment variable.
00435          */
00436         if (!Gvfilepath) {
00437             if (onetime) {
00438                 agerr(AGWARN,
00439                       "file loading is disabled because the environment contains SERVER_NAME=\"%s\"n"
00440                       "and there is no GV_FILE_PATH variable set.\n",
00441                       HTTPServerEnVar);
00442                 onetime = FALSE;
00443             }
00444             return NULL;
00445         }
00446         if (!pathlist) {
00447             dirs = mkDirlist (Gvfilepath, &maxdirlen);
00448             pathlist = Gvfilepath;
00449         }
00450 
00451         str = filename;
00452         if ((p = strrchr(str, '/')))
00453             str = ++p;
00454         if ((p = strrchr(str, '\\')))
00455             str = ++p;
00456         if ((p = strrchr(str, ':')))
00457             str = ++p;
00458 
00459         if (onetime && str != filename) {
00460             agerr(AGWARN, "Path provided to file: \"%s\" has been ignored"
00461                   " because files are only permitted to be loaded from the directories in \"%s\""
00462                   " when running in an http server.\n", filename, Gvfilepath);
00463             onetime = FALSE;
00464         }
00465 
00466         return findPath (dirs, maxdirlen, str);
00467     }
00468 
00469     if (pathlist != Gvimagepath) {
00470         if (dirs) {
00471             free (dirs[0]);
00472             free (dirs);
00473             dirs = NULL;
00474         }
00475         pathlist = Gvimagepath;
00476         if (pathlist && *pathlist)
00477             dirs = mkDirlist (pathlist, &maxdirlen);
00478     }
00479 
00480     if ((*filename == DIRSEP[0]) || !dirs)
00481         return filename;
00482 
00483     return findPath (dirs, maxdirlen, filename);
00484 }
00485 
00486 int maptoken(char *p, char **name, int *val)
00487 {
00488     int i;
00489     char *q;
00490 
00491     for (i = 0; (q = name[i]) != 0; i++)
00492         if (p && streq(p, q))
00493             break;
00494     return val[i];
00495 }
00496 
00497 boolean mapBool(char *p, boolean dflt)
00498 {
00499     if (!p || (*p == '\0'))
00500         return dflt;
00501     if (!strcasecmp(p, "false"))
00502         return FALSE;
00503     if (!strcasecmp(p, "no"))
00504         return FALSE;
00505     if (!strcasecmp(p, "true"))
00506         return TRUE;
00507     if (!strcasecmp(p, "yes"))
00508         return TRUE;
00509     if (isdigit(*p))
00510         return atoi(p);
00511     else
00512         return dflt;
00513 }
00514 
00515 boolean mapbool(char *p)
00516 {
00517     return mapBool (p, FALSE);
00518 }
00519 
00520 pointf dotneato_closest(splines * spl, pointf pt)
00521 {
00522     int i, j, k, besti, bestj;
00523     double bestdist2, d2, dlow2, dhigh2; /* squares of distances */
00524     double low, high, t;
00525     pointf c[4], pt2;
00526     bezier bz;
00527 
00528     besti = bestj = -1;
00529     bestdist2 = 1e+38;
00530     for (i = 0; i < spl->size; i++) {
00531         bz = spl->list[i];
00532         for (j = 0; j < bz.size; j++) {
00533             pointf b;
00534 
00535             b.x = bz.list[j].x;
00536             b.y = bz.list[j].y;
00537             d2 = DIST2(b, pt);
00538             if ((bestj == -1) || (d2 < bestdist2)) {
00539                 besti = i;
00540                 bestj = j;
00541                 bestdist2 = d2;
00542             }
00543         }
00544     }
00545 
00546     bz = spl->list[besti];
00547     /* Pick best Bezier. If bestj is the last point in the B-spline, decrement.
00548      * Then set j to be the first point in the corresponding Bezier by dividing
00549      * then multiplying be 3. Thus, 0,1,2 => 0; 3,4,5 => 3, etc.
00550      */
00551     if (bestj == bz.size-1)
00552         bestj--;
00553     j = 3*(bestj / 3);
00554     for (k = 0; k < 4; k++) {
00555         c[k].x = bz.list[j + k].x;
00556         c[k].y = bz.list[j + k].y;
00557     }
00558     low = 0.0;
00559     high = 1.0;
00560     dlow2 = DIST2(c[0], pt);
00561     dhigh2 = DIST2(c[3], pt);
00562     do {
00563         t = (low + high) / 2.0;
00564         pt2 = Bezier(c, 3, t, NULL, NULL);
00565         if (fabs(dlow2 - dhigh2) < 1.0)
00566             break;
00567         if (fabs(high - low) < .00001)
00568             break;
00569         if (dlow2 < dhigh2) {
00570             high = t;
00571             dhigh2 = DIST2(pt2, pt);
00572         } else {
00573             low = t;
00574             dlow2 = DIST2(pt2, pt);
00575         }
00576     } while (1);
00577     return pt2;
00578 }
00579 
00580 pointf spline_at_y(splines * spl, double y)
00581 {
00582     int i, j;
00583     double low, high, d, t;
00584     pointf c[4], p;
00585     static bezier bz;
00586 
00587 /* this caching seems to prevent p.x from getting set from bz.list[0].x
00588         - optimizer problem ? */
00589 
00590 #if 0
00591     static splines *mem = NULL;
00592 
00593     if (mem != spl) {
00594         mem = spl;
00595 #endif
00596         for (i = 0; i < spl->size; i++) {
00597             bz = spl->list[i];
00598             if (BETWEEN(bz.list[bz.size - 1].y, y, bz.list[0].y))
00599                 break;
00600         }
00601 #if 0
00602     }
00603 #endif
00604     if (y > bz.list[0].y)
00605         p = bz.list[0];
00606     else if (y < bz.list[bz.size - 1].y)
00607         p = bz.list[bz.size - 1];
00608     else {
00609         for (i = 0; i < bz.size; i += 3) {
00610             for (j = 0; j < 3; j++) {
00611                 if ((bz.list[i + j].y <= y) && (y <= bz.list[i + j + 1].y))
00612                     break;
00613                 if ((bz.list[i + j].y >= y) && (y >= bz.list[i + j + 1].y))
00614                     break;
00615             }
00616             if (j < 3)
00617                 break;
00618         }
00619         assert(i < bz.size);
00620         for (j = 0; j < 4; j++) {
00621             c[j].x = bz.list[i + j].x;
00622             c[j].y = bz.list[i + j].y;
00623             /* make the spline be monotonic in Y, awful but it works for now */
00624             if ((j > 0) && (c[j].y > c[j - 1].y))
00625                 c[j].y = c[j - 1].y;
00626         }
00627         low = 0.0;
00628         high = 1.0;
00629         do {
00630             t = (low + high) / 2.0;
00631             p = Bezier(c, 3, t, NULL, NULL);
00632             d = p.y - y;
00633             if (ABS(d) <= 1)
00634                 break;
00635             if (d < 0)
00636                 high = t;
00637             else
00638                 low = t;
00639         } while (1);
00640     }
00641     p.y = y;
00642     return p;
00643 }
00644 
00645 pointf neato_closest(splines * spl, pointf p)
00646 {
00647 /* this is a stub so that we can share a common emit.c between dot and neato */
00648 
00649     return spline_at_y(spl, p.y);
00650 }
00651 
00652 static int Tflag;
00653 void gvToggle(int s)
00654 {
00655     Tflag = !Tflag;
00656 #if !defined(MSWIN32) && !defined(WIN32)
00657     signal(SIGUSR1, gvToggle);
00658 #endif
00659 }
00660 
00661 int test_toggle()
00662 {
00663     return Tflag;
00664 }
00665 
00666 struct fontinfo {
00667     double fontsize;
00668     char *fontname;
00669     char *fontcolor;
00670 };
00671 
00672 void common_init_node(node_t * n)
00673 {
00674     struct fontinfo fi;
00675     char *str;
00676     ND_width(n) =
00677         late_double(n, N_width, DEFAULT_NODEWIDTH, MIN_NODEWIDTH);
00678     ND_height(n) =
00679         late_double(n, N_height, DEFAULT_NODEHEIGHT, MIN_NODEHEIGHT);
00680     ND_shape(n) =
00681         bind_shape(late_nnstring(n, N_shape, DEFAULT_NODESHAPE), n);
00682 #ifndef WITH_CGRAPH
00683     str = agxget(n, N_label->index);
00684 #else
00685     str = agxget(n, N_label);
00686 #endif
00687     fi.fontsize = late_double(n, N_fontsize, DEFAULT_FONTSIZE, MIN_FONTSIZE);
00688     fi.fontname = late_nnstring(n, N_fontname, DEFAULT_FONTNAME);
00689     fi.fontcolor = late_nnstring(n, N_fontcolor, DEFAULT_COLOR);
00690     ND_label(n) = make_label((void*)n, str,
00691                 ((aghtmlstr(str) ? LT_HTML : LT_NONE) | ( (shapeOf(n) == SH_RECORD) ? LT_RECD : LT_NONE)),
00692                 fi.fontsize, fi.fontname, fi.fontcolor);
00693 #ifndef WITH_CGRAPH
00694     if (N_xlabel && (str = agxget(n, N_xlabel->index)) && (str[0])) {
00695 #else
00696     if (N_xlabel && (str = agxget(n, N_xlabel)) && (str[0])) {
00697 #endif
00698         ND_xlabel(n) = make_label((void*)n, str, (aghtmlstr(str) ? LT_HTML : LT_NONE),
00699                                 fi.fontsize, fi.fontname, fi.fontcolor);
00700         GD_has_labels(agraphof(n)) |= NODE_XLABEL;
00701     }
00702 
00703     ND_showboxes(n) = late_int(n, N_showboxes, 0, 0);
00704     ND_shape(n)->fns->initfn(n);
00705 }
00706 
00707 static void initFontEdgeAttr(edge_t * e, struct fontinfo *fi)
00708 {
00709     fi->fontsize = late_double(e, E_fontsize, DEFAULT_FONTSIZE, MIN_FONTSIZE);
00710     fi->fontname = late_nnstring(e, E_fontname, DEFAULT_FONTNAME);
00711     fi->fontcolor = late_nnstring(e, E_fontcolor, DEFAULT_COLOR);
00712 }
00713 
00714 static void
00715 initFontLabelEdgeAttr(edge_t * e, struct fontinfo *fi,
00716                       struct fontinfo *lfi)
00717 {
00718     if (!fi->fontname) initFontEdgeAttr(e, fi);
00719     lfi->fontsize = late_double(e, E_labelfontsize, fi->fontsize, MIN_FONTSIZE);
00720     lfi->fontname = late_nnstring(e, E_labelfontname, fi->fontname);
00721     lfi->fontcolor = late_nnstring(e, E_labelfontcolor, fi->fontcolor);
00722 }
00723 
00724 /* noClip:
00725  * Return true if head/tail end of edge should not be clipped
00726  * to node.
00727  */
00728 static boolean 
00729 noClip(edge_t *e, attrsym_t* sym)
00730 {
00731     char                *str;
00732     boolean             rv = FALSE;
00733 
00734     if (sym) {  /* mapbool isn't a good fit, because we want "" to mean true */
00735 #ifndef WITH_CGRAPH
00736         str = agxget(e,sym->index);
00737 #else /* WITH_CGRAPH */
00738         str = agxget(e,sym);
00739 #endif /* WITH_CGRAPH */
00740         if (str && str[0]) rv = !mapbool(str);
00741         else rv = FALSE;
00742     }
00743     return rv;
00744 }
00745 
00746 /*chkPort:
00747  */
00748 static port
00749 chkPort (port (*pf)(node_t*, char*, char*), node_t* n, char* s)
00750 { 
00751     port pt;
00752 #ifndef WITH_CGRAPH
00753     char* cp = strchr(s,':');
00754 
00755 #else /* WITH_CGRAPH */
00756         char* cp=NULL;
00757         if(s)
00758                 cp= strchr(s,':');
00759 #endif /* WITH_CGRAPH */
00760     if (cp) {
00761         *cp = '\0';
00762         pt = pf(n, s, cp+1);
00763         *cp = ':';
00764         pt.name = cp+1;
00765     }
00766     else
00767         pt = pf(n, s, NULL);
00768         pt.name = s;
00769     return pt;
00770 }
00771 
00772 /* return true if edge has label */
00773 int common_init_edge(edge_t * e)
00774 {
00775     char *str;
00776     int r = 0;
00777     struct fontinfo fi;
00778     struct fontinfo lfi;
00779     graph_t *sg = agraphof(agtail(e));
00780 
00781     fi.fontname = NULL;
00782     lfi.fontname = NULL;
00783 #ifndef WITH_CGRAPH
00784     if (E_label && (str = agxget(e, E_label->index)) && (str[0])) {
00785 #else
00786     if (E_label && (str = agxget(e, E_label)) && (str[0])) {
00787 #endif
00788         r = 1;
00789         initFontEdgeAttr(e, &fi);
00790         ED_label(e) = make_label((void*)e, str, (aghtmlstr(str) ? LT_HTML : LT_NONE),
00791                                 fi.fontsize, fi.fontname, fi.fontcolor);
00792         GD_has_labels(sg) |= EDGE_LABEL;
00793         ED_label_ontop(e) =
00794             mapbool(late_string(e, E_label_float, "false"));
00795     }
00796 
00797 #ifndef WITH_CGRAPH
00798     if (E_xlabel && (str = agxget(e, E_xlabel->index)) && (str[0])) {
00799 #else
00800     if (E_xlabel && (str = agxget(e, E_xlabel)) && (str[0])) {
00801 #endif
00802         if (!fi.fontname)
00803             initFontEdgeAttr(e, &fi);
00804         ED_xlabel(e) = make_label((void*)e, str, (aghtmlstr(str) ? LT_HTML : LT_NONE),
00805                                 fi.fontsize, fi.fontname, fi.fontcolor);
00806         GD_has_labels(sg) |= EDGE_XLABEL;
00807     }
00808 
00809 
00810     /* vladimir */
00811 #ifndef WITH_CGRAPH
00812     if (E_headlabel && (str = agxget(e, E_headlabel->index)) && (str[0])) {
00813 #else
00814     if (E_headlabel && (str = agxget(e, E_headlabel)) && (str[0])) {
00815 #endif
00816         initFontLabelEdgeAttr(e, &fi, &lfi);
00817         ED_head_label(e) = make_label((void*)e, str, (aghtmlstr(str) ? LT_HTML : LT_NONE),
00818                                 lfi.fontsize, lfi.fontname, lfi.fontcolor);
00819         GD_has_labels(sg) |= HEAD_LABEL;
00820     }
00821 #ifndef WITH_CGRAPH
00822     if (E_taillabel && (str = agxget(e, E_taillabel->index)) && (str[0])) {
00823 #else
00824     if (E_taillabel && (str = agxget(e, E_taillabel)) && (str[0])) {
00825 #endif
00826         if (!lfi.fontname)
00827             initFontLabelEdgeAttr(e, &fi, &lfi);
00828         ED_tail_label(e) = make_label((void*)e, str, (aghtmlstr(str) ? LT_HTML : LT_NONE),
00829                                 lfi.fontsize, lfi.fontname, lfi.fontcolor);
00830         GD_has_labels(sg) |= TAIL_LABEL;
00831     }
00832     /* end vladimir */
00833 
00834     /* We still accept ports beginning with colons but this is deprecated 
00835      * That is, we allow tailport = ":abc" as well as the preferred 
00836      * tailport = "abc".
00837      */
00838     str = agget(e, TAIL_ID);
00839 #ifdef WITH_CGRAPH
00840     /* libgraph always defines tailport/headport; libcgraph doesn't */
00841     if (!str) str = "";
00842 #endif
00843     if (str && str[0])
00844         ND_has_port(agtail(e)) = TRUE;
00845     ED_tail_port(e) = chkPort (ND_shape(agtail(e))->fns->portfn, agtail(e), str);
00846     if (noClip(e, E_tailclip))
00847         ED_tail_port(e).clip = FALSE;
00848     str = agget(e, HEAD_ID);
00849 #ifdef WITH_CGRAPH
00850     /* libgraph always defines tailport/headport; libcgraph doesn't */
00851     if (!str) str = "";
00852 #endif
00853     if (str && str[0])
00854         ND_has_port(aghead(e)) = TRUE;
00855     ED_head_port(e) = chkPort(ND_shape(aghead(e))->fns->portfn, aghead(e), str);
00856     if (noClip(e, E_headclip))
00857         ED_head_port(e).clip = FALSE;
00858 
00859     return r;
00860 }
00861 
00862 /* addLabelBB:
00863  */
00864 static boxf addLabelBB(boxf bb, textlabel_t * lp, boolean flipxy)
00865 {
00866     double width, height;
00867     pointf p = lp->pos;
00868     double min, max;
00869 
00870     if (flipxy) {
00871         height = lp->dimen.x;
00872         width = lp->dimen.y;
00873     }
00874     else {
00875         width = lp->dimen.x;
00876         height = lp->dimen.y;
00877     }
00878     min = p.x - width / 2.;
00879     max = p.x + width / 2.;
00880     if (min < bb.LL.x)
00881         bb.LL.x = min;
00882     if (max > bb.UR.x)
00883         bb.UR.x = max;
00884 
00885     min = p.y - height / 2.;
00886     max = p.y + height / 2.;
00887     if (min < bb.LL.y)
00888         bb.LL.y = min;
00889     if (max > bb.UR.y)
00890         bb.UR.y = max;
00891 
00892     return bb;
00893 }
00894 
00895 /* updateBB:
00896  * Reset graph's bounding box to include bounding box of the given label.
00897  * Assume the label's position has been set.
00898  */
00899 void updateBB(graph_t * g, textlabel_t * lp)
00900 {
00901     GD_bb(g) = addLabelBB(GD_bb(g), lp, GD_flip(g));
00902 }
00903 
00904 /* compute_bb:
00905  * Compute bounding box of g using nodes, splines, and clusters.
00906  * Assumes bb of clusters already computed.
00907  * store in GD_bb.
00908  */
00909 void compute_bb(graph_t * g)
00910 {
00911     node_t *n;
00912     edge_t *e;
00913     boxf b, bb;
00914     boxf BF;
00915     pointf ptf, s2;
00916     int i, j;
00917 
00918     if ((agnnodes(g) == 0) && (GD_n_cluster(g) ==0)) {
00919         bb.LL = pointfof(0, 0);
00920         bb.UR = pointfof(0, 0);
00921         return;
00922     }
00923 
00924     bb.LL = pointfof(INT_MAX, INT_MAX);
00925     bb.UR = pointfof(-INT_MAX, -INT_MAX);
00926     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
00927         ptf = coord(n);
00928         s2.x = ND_xsize(n) / 2.0;
00929         s2.y = ND_ysize(n) / 2.0;
00930         b.LL = sub_pointf(ptf, s2);
00931         b.UR = add_pointf(ptf, s2);
00932 
00933         EXPANDBB(bb,b);
00934         for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
00935             if (ED_spl(e) == 0)
00936                 continue;
00937             for (i = 0; i < ED_spl(e)->size; i++) {
00938 #ifndef WITH_CGRAPH
00939                 for (j = 0; j < ED_spl(e)->list[i].size; j++) {
00940 #else
00941                 for (j = 0; j < (((Agedgeinfo_t*)AGDATA(e))->spl)->list[i].size; j++) {
00942 #endif
00943                     ptf = ED_spl(e)->list[i].list[j];
00944                     EXPANDBP(bb,ptf);
00945                 }
00946             }
00947             if (ED_label(e) && ED_label(e)->set) {
00948                 bb = addLabelBB(bb, ED_label(e), GD_flip(g));
00949             }
00950         }
00951     }
00952 
00953     for (i = 1; i <= GD_n_cluster(g); i++) {
00954         B2BF(GD_bb(GD_clust(g)[i]), BF);
00955         EXPANDBB(bb,BF);
00956     }
00957     if (GD_label(g) && GD_label(g)->set) {
00958         bb = addLabelBB(bb, GD_label(g), GD_flip(g));
00959     }
00960 
00961     GD_bb(g) = bb;
00962 }
00963 
00964 int is_a_cluster (Agraph_t* g)
00965 {
00966     return ((g == g->root) || (!strncasecmp(agnameof(g), "cluster", 7)));
00967 }
00968 
00969 /* setAttr:
00970  * Sets object's name attribute to the given value.
00971  * Creates the attribute if not already set.
00972  */
00973 Agsym_t *setAttr(graph_t * g, void *obj, char *name, char *value,
00974                         Agsym_t * ap)
00975 {
00976     if (ap == NULL) {
00977         switch (agobjkind(obj)) {
00978 #ifndef WITH_CGRAPH
00979         case AGGRAPH:
00980             ap = agraphattr(g, name, "");
00981 #else /* WITH_CGRAPH */
00982         case AGRAPH:
00983             ap = agattr(g, AGRAPH,name, "");
00984 #endif /* WITH_CGRAPH */
00985             break;
00986         case AGNODE:
00987 #ifndef WITH_CGRAPH
00988             ap = agnodeattr(g, name, "");
00989 #else /* WITH_CGRAPH */
00990             ap = agattr(g,AGNODE, name, "");
00991 #endif /* WITH_CGRAPH */
00992             break;
00993         case AGEDGE:
00994 #ifndef WITH_CGRAPH
00995             ap = agedgeattr(g, name, "");
00996 #else /* WITH_CGRAPH */
00997             ap = agattr(g,AGEDGE, name, "");
00998 #endif /* WITH_CGRAPH */
00999             break;
01000         }
01001     }
01002 #ifndef WITH_CGRAPH
01003     agxset(obj, ap->index, value);
01004 #else /* WITH_CGRAPH */
01005     agxset(obj, ap, value);
01006 #endif /* WITH_CGRAPH */
01007     return ap;
01008 }
01009 
01010 /* clustNode:
01011  * Generate a special cluster node representing the end node
01012  * of an edge to the cluster cg. n is a node whose name is the same
01013  * as the cluster cg. clg is the subgraph of all of
01014  * the original nodes, which will be deleted later.
01015  */
01016 static node_t *clustNode(node_t * n, graph_t * cg, agxbuf * xb,
01017                          graph_t * clg)
01018 {
01019     node_t *cn;
01020     static int idx = 0;
01021     char num[100];
01022 
01023     agxbput(xb, "__");
01024     sprintf(num, "%d", idx++);
01025     agxbput(xb, num);
01026     agxbputc(xb, ':');
01027     agxbput(xb, agnameof(cg));
01028 
01029 #ifndef WITH_CGRAPH
01030     cn = agnode(agroot(cg), agxbuse(xb));
01031 #else
01032     cn = agnode(agroot(cg), agxbuse(xb), 1);
01033     agbindrec(cn, "Agnodeinfo_t", sizeof(Agnodeinfo_t), TRUE);
01034 #endif /* WITH_CGRAPH */
01035 
01036     SET_CLUST_NODE(cn);
01037 #ifndef WITH_CGRAPH
01038     aginsert(cg, cn);
01039     aginsert(clg, n);
01040 #else /* WITH_CGRAPH */
01041         agsubnode(cg,cn,1);
01042         //aginsert(cg, cn);
01043         agsubnode(clg,n,1);
01044         //aginsert(clg, n);
01045 #endif /* WITH_CGRAPH */
01046 
01047     /* set attributes */
01048     N_label = setAttr(agraphof(cn), cn, "label", "", N_label);
01049     N_style = setAttr(agraphof(cn), cn, "style", "invis", N_style);
01050     N_shape = setAttr(agraphof(cn), cn, "shape", "box", N_shape);
01051     /* N_width = setAttr (cn->graph, cn, "width", "0.0001", N_width); */
01052 
01053     return cn;
01054 }
01055 
01056 typedef struct {
01057     Dtlink_t link;              /* cdt data */
01058     void *p[2];                 /* key */
01059     node_t *t;
01060     node_t *h;
01061 } item;
01062 
01063 static int cmpItem(Dt_t * d, void *p1[], void *p2[], Dtdisc_t * disc)
01064 {
01065     NOTUSED(d);
01066     NOTUSED(disc);
01067 
01068     if (p1[0] < p2[0])
01069         return -1;
01070     else if (p1[0] > p2[0])
01071         return 1;
01072     else if (p1[1] < p2[1])
01073         return -1;
01074     else if (p1[1] > p2[1])
01075         return 1;
01076     else
01077         return 0;
01078 }
01079 
01080 /* newItem:
01081  */
01082 static void *newItem(Dt_t * d, item * objp, Dtdisc_t * disc)
01083 {
01084     item *newp = NEW(item);
01085 
01086     NOTUSED(disc);
01087     newp->p[0] = objp->p[0];
01088     newp->p[1] = objp->p[1];
01089     newp->t = objp->t;
01090     newp->h = objp->h;
01091 
01092     return newp;
01093 }
01094 
01095 /* freeItem:
01096  */
01097 static void freeItem(Dt_t * d, item * obj, Dtdisc_t * disc)
01098 {
01099     free(obj);
01100 }
01101 
01102 static Dtdisc_t mapDisc = {
01103     offsetof(item, p),
01104     sizeof(2 * sizeof(void *)),
01105     offsetof(item, link),
01106     (Dtmake_f) newItem,
01107     (Dtfree_f) freeItem,
01108     (Dtcompar_f) cmpItem,
01109     NIL(Dthash_f),
01110     NIL(Dtmemory_f),
01111     NIL(Dtevent_f)
01112 };
01113 
01114 /* cloneEdge:
01115  * Make a copy of e in e's graph but using ct and ch as nodes
01116  */
01117 static edge_t *cloneEdge(edge_t * e, node_t * ct, node_t * ch)
01118 {
01119     graph_t *g = agraphof(ct);
01120 #ifndef WITH_CGRAPH
01121     edge_t *ce = agedge(g, ct, ch);
01122 #else /* WITH_CGRAPH */
01123     edge_t *ce = agedge(g, ct, ch,NULL,1);
01124     agbindrec(ce, "Agedgeinfo_t", sizeof(Agedgeinfo_t), TRUE);
01125 #endif /* WITH_CGRAPH */
01126     agcopyattr(e, ce);
01127 
01128     return ce;
01129 }
01130 
01131 /* insertEdge:
01132  */
01133 static void insertEdge(Dt_t * map, void *t, void *h, edge_t * e)
01134 {
01135     item dummy;
01136 
01137     dummy.p[0] = t;
01138     dummy.p[1] = h;
01139     dummy.t = agtail(e);
01140     dummy.h = aghead(e);
01141     dtinsert(map, &dummy);
01142 
01143     dummy.p[0] = h;
01144     dummy.p[1] = t;
01145     dummy.t = aghead(e);
01146     dummy.h = agtail(e);
01147     dtinsert(map, &dummy);
01148 }
01149 
01150 /* mapEdge:
01151  * Check if we already have cluster edge corresponding to t->h,
01152  * and return it.
01153  */
01154 static item *mapEdge(Dt_t * map, edge_t * e)
01155 {
01156     void *key[2];
01157 
01158     key[0] = agtail(e);
01159     key[1] = aghead(e);
01160     return (item *) dtmatch(map, &key);
01161 }
01162 
01163 /* checkCompound:
01164  * If endpoint names a cluster, mark for temporary deletion and create 
01165  * special node and insert into cluster. Then clone the edge. Real edge
01166  * will be deleted when we delete the original node.
01167  * Invariant: new edge has same sense as old. That is, given t->h with
01168  * t and h mapped to ct and ch, the new edge is ct->ch.
01169  *
01170  * In the current model, we create a cluster node for each cluster edge
01171  * between the cluster and some other node or cluster, treating the
01172  * cluster node as a port on the cluster. This should help with better
01173  * routing to avoid edge crossings. At present, this is not implemented,
01174  * so we could use a simpler model in which we create a single cluster
01175  * node for each cluster used in a cluster edge.
01176  */
01177 #ifndef WITH_CGRAPH
01178 #define MAPC(n) (strncmp((n)->name,"cluster",7)?NULL:agfindsubg((n)->graph, (n)->name))
01179 #else /* WITH_CGRAPH */
01180 #define MAPC(n) (strncmp(agnameof(n),"cluster",7)?NULL:findCluster(cmap,agnameof(n)))
01181 #endif /* WITH_CGRAPH */
01182 
01183 
01184 #ifdef WITH_CGRAPH
01185 static void
01186 checkCompound(edge_t * e, graph_t * clg, agxbuf * xb, Dt_t * map, Dt_t* cmap)
01187 #else
01188 static void
01189 checkCompound(edge_t * e, graph_t * clg, agxbuf * xb, Dt_t * map)
01190 #endif
01191 {
01192     graph_t *tg;
01193     graph_t *hg;
01194     node_t *cn;
01195     node_t *cn1;
01196     node_t *t = agtail(e);
01197     node_t *h = aghead(e);
01198     edge_t *ce;
01199     item *ip;
01200 
01201     if (IS_CLUST_NODE(h)) return;
01202     tg = MAPC(t);
01203     hg = MAPC(h);
01204     if (!tg && !hg)
01205         return;
01206     if (tg == hg) {
01207         agerr(AGWARN, "cluster cycle %s -- %s not supported\n", agnameof(t),
01208               agnameof(t));
01209         return;
01210     }
01211     ip = mapEdge(map, e);
01212     if (ip) {
01213         cloneEdge(e, ip->t, ip->h);
01214         return;
01215     }
01216 
01217     if (hg) {
01218         if (tg) {
01219             if (agcontains(hg, tg)) {
01220                 agerr(AGWARN, "tail cluster %s inside head cluster %s\n",
01221                       agnameof(tg), agnameof(hg));
01222                 return;
01223             }
01224             if (agcontains(tg, hg)) {
01225                 agerr(AGWARN, "head cluster %s inside tail cluster %s\n",
01226                       agnameof(hg),agnameof(tg));
01227                 return;
01228             }
01229             cn = clustNode(t, tg, xb, clg);
01230             cn1 = clustNode(h, hg, xb, clg);
01231             ce = cloneEdge(e, cn, cn1);
01232             insertEdge(map, t, h, ce);
01233         } else {
01234             if (agcontains(hg, t)) {
01235                 agerr(AGWARN, "tail node %s inside head cluster %s\n",
01236                       agnameof(t), agnameof(hg));
01237                 return;
01238             }
01239             cn = clustNode(h, hg, xb, clg);
01240             ce = cloneEdge(e, t, cn);
01241             insertEdge(map, t, h, ce);
01242         }
01243     } else {
01244         if (agcontains(tg, h)) {
01245             agerr(AGWARN, "head node %s inside tail cluster %s\n", agnameof(h),
01246                   agnameof(tg));
01247             return;
01248         }
01249         cn = clustNode(t, tg, xb, clg);
01250         ce = cloneEdge(e, cn, h);
01251         insertEdge(map, t, h, ce);
01252     }
01253 }
01254 
01255 /* processClusterEdges:
01256  * Look for cluster edges. Replace cluster edge endpoints
01257  * corresponding to a cluster with special cluster nodes.
01258  * Delete original nodes.
01259  * Return 0 if no cluster edges; 1 otherwise.
01260  */
01261 int processClusterEdges(graph_t * g)
01262 {
01263     int rv;
01264     node_t *n;
01265     node_t *nxt;
01266     edge_t *e;
01267     graph_t *clg;
01268     agxbuf xb;
01269     Dt_t *map;
01270 #ifdef WITH_CGRAPH
01271     Dt_t *cmap = mkClustMap (g);
01272 #endif
01273     unsigned char buf[SMALLBUF];
01274 
01275     map = dtopen(&mapDisc, Dtoset);
01276 #ifndef WITH_CGRAPH
01277     clg = agsubg(g, "__clusternodes");
01278 #else /* WITH_CGRAPH */
01279     clg = agsubg(g, "__clusternodes",1);
01280     agbindrec(clg, "Agraphinfo_t", sizeof(Agraphinfo_t), TRUE);
01281 #endif /* WITH_CGRAPH */
01282     agxbinit(&xb, SMALLBUF, buf);
01283     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
01284         if (IS_CLUST_NODE(n)) continue;
01285         for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
01286 #ifndef WITH_CGRAPH
01287             checkCompound(e, clg, &xb, map);
01288 #else
01289             checkCompound(e, clg, &xb, map, cmap);
01290 #endif
01291         }
01292     }
01293     agxbfree(&xb);
01294     dtclose(map);
01295     rv = agnnodes(clg);
01296     for (n = agfstnode(clg); n; n = nxt) {
01297         nxt = agnxtnode(clg, n);
01298         agdelete(g, n);
01299     }
01300     agclose(clg);
01301     if (rv)
01302         SET_CLUST_EDGE(g);
01303 #ifdef WITH_CGRAPH
01304     dtclose(cmap);
01305 #endif
01306     return rv;
01307 }
01308 
01309 /* mapN:
01310  * Convert cluster nodes back to ordinary nodes
01311  * If n is already ordinary, return it.
01312  * Otherwise, we know node's name is "__i:xxx"
01313  * where i is some number and xxx is the nodes's original name.
01314  * Create new node of name xxx if it doesn't exist and add n to clg
01315  * for later deletion.
01316  */
01317 static node_t *mapN(node_t * n, graph_t * clg)
01318 {
01319 #ifndef WITH_CGRAPH
01320     extern Agdict_t *agdictof(void *);
01321     Agdict_t *d;
01322     Agsym_t **list;
01323 #endif /* WITH_CGRAPH */
01324     node_t *nn;
01325     char *name;
01326     graph_t *g = agraphof(n);
01327     Agsym_t *sym;
01328 
01329     if (!(IS_CLUST_NODE(n)))
01330         return n;
01331 #ifndef WITH_CGRAPH
01332     aginsert(clg, n);
01333 #else /* WITH_CGRAPH */
01334     agsubnode(clg, n, 1);
01335 #endif /* WITH_CGRAPH */
01336     name = strchr(agnameof(n), ':');
01337     assert(name);
01338     name++;
01339     if ((nn = agfindnode(g, name)))
01340         return nn;
01341 #ifndef WITH_CGRAPH
01342     nn = agnode(g, name);
01343 #else /* WITH_CGRAPH */
01344     nn = agnode(g, name, 1);
01345     agbindrec(nn, "Agnodeinfo_t", sizeof(Agnodeinfo_t), TRUE);
01346 #endif /* WITH_CGRAPH */
01347 
01348     /* Set all attributes to default */
01349 #ifndef WITH_CGRAPH
01350     d = agdictof(n);
01351     list = d->list;
01352     while ((sym = *list++)) {
01353         /* Can use pointer comparison because of ref strings. */
01354         if (agxget(nn, sym->index) != sym->value)
01355             agxset(nn, sym->index, sym->value);
01356     }
01357 #else /* WITH_CGRAPH */
01358     for (sym = agnxtattr(g, AGNODE, NULL); sym;  (sym = agnxtattr(g, AGNODE, sym))) {
01359         if (agxget(nn, sym) != sym->defval)
01360             agxset(nn, sym, sym->defval);
01361     }
01362 #endif /* WITH_CGRAPH */
01363     return nn;
01364 }
01365 
01366 static void undoCompound(edge_t * e, graph_t * clg)
01367 {
01368     node_t *t = agtail(e);
01369     node_t *h = aghead(e);
01370     node_t *ntail;
01371     node_t *nhead;
01372 
01373     if (!(IS_CLUST_NODE(t) || IS_CLUST_NODE(h)))
01374         return;
01375     ntail = mapN(t, clg);
01376     nhead = mapN(h, clg);
01377     cloneEdge(e, ntail, nhead);
01378 }
01379 
01380 /* undoClusterEdges:
01381  * Replace cluster nodes with originals. Make sure original has
01382  * no attributes. Replace original edges. Delete cluster nodes,
01383  * which will also delete cluster edges.
01384  */
01385 void undoClusterEdges(graph_t * g)
01386 {
01387     node_t *n;
01388     edge_t *e;
01389     graph_t *clg;
01390 
01391 #ifndef WITH_CGRAPH
01392     clg = agsubg(g, "__clusternodes");
01393 #else /* WITH_CGRAPH */
01394     clg = agsubg(g, "__clusternodes",1);
01395         agbindrec(clg, "Agraphinfo_t", sizeof(Agraphinfo_t), TRUE);
01396 #endif /* WITH_CGRAPH */
01397     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
01398         for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
01399             undoCompound(e, clg);
01400         }
01401     }
01402     for (n = agfstnode(clg); n; n = agnxtnode(clg, n)) {
01403         agdelete(g, n);
01404     }
01405     agclose(clg);
01406 }
01407 
01408 /* safe_dcl:
01409  * Find the attribute belonging to graph g for objects like obj
01410  * with given name. If one does not exist, create it with the
01411  * default value def.
01412  */ 
01413 #ifndef WITH_CGRAPH
01414 attrsym_t*
01415 safe_dcl(graph_t * g, void *obj, char *name, char *def,
01416          attrsym_t * (*fun) (Agraph_t *, char *, char *))
01417 {
01418     attrsym_t *a = agfindattr(obj, name);
01419     if (a == NULL)
01420         a = fun(g, name, def);
01421     return a;
01422 }
01423 #else /* WITH_CGRAPH */
01424 attrsym_t*
01425 safe_dcl(graph_t * g, int obj_kind, char *name, char *def)
01426 {
01427     attrsym_t *a = agattr(g,obj_kind,name, NULL);
01428     if (!a)     /* attribute does not exist */          
01429         a = agattr(g,obj_kind,name,def);
01430     return a;
01431 }
01432 #endif /* WITH_CGRAPH */
01433 
01434 static int comp_entities(const void *e1, const void *e2) {
01435   return strcmp(((struct entities_s *)e1)->name, ((struct entities_s *)e2)->name);
01436 }
01437 
01438 #define MAXENTLEN 8
01439 
01440 /* scanEntity:
01441  * Scan non-numeric entity, convert to &#...; form and store in xbuf.
01442  * t points to first char after '&'. Return after final semicolon.
01443  * If unknown, we return t and let libexpat flag the error.
01444  *     */
01445 char* scanEntity (char* t, agxbuf* xb)
01446 {
01447     char*  endp = strchr (t, ';');
01448     struct entities_s key, *res;
01449     int    len;
01450     char   buf[MAXENTLEN+1];
01451 
01452     agxbputc(xb, '&');
01453     if (!endp) return t;
01454     if (((len = endp-t) > MAXENTLEN) || (len < 2)) return t;
01455     strncpy (buf, t, len);
01456     buf[len] = '\0';
01457     key.name =  buf;
01458     res = bsearch(&key, entities, NR_OF_ENTITIES,
01459         sizeof(entities[0]), comp_entities);
01460     if (!res) return t;
01461     sprintf (buf, "%d", res->value);
01462     agxbputc(xb, '#');
01463     agxbput(xb, buf);
01464     agxbputc(xb, ';');
01465     return (endp+1);
01466 }
01467 
01468 
01469 /* htmlEntity:
01470  * Check for an HTML entity for a special character.
01471  * Assume *s points to first byte after '&'. 
01472  * If successful, return the corresponding value and update s to
01473  * point after the terminating ';'.
01474  * On failure, return 0 and leave s unchanged.
01475  */
01476 static int
01477 htmlEntity (char** s)
01478 {
01479     char *p;
01480     struct entities_s key, *res;
01481     char entity_name_buf[ENTITY_NAME_LENGTH_MAX+1];
01482     unsigned char* str = *(unsigned char**)s;
01483     unsigned int byte;
01484     int i, n = 0;
01485 
01486     byte = *str;
01487     if (byte == '#') {
01488         byte = *(str + 1);
01489         if (byte == 'x' || byte == 'X') {
01490             for (i = 2; i < 8; i++) {
01491                 byte = *(str + i);
01492                 if (byte >= 'A' && byte <= 'F')
01493                     byte = byte - 'A' + 10;
01494                 else if (byte >= 'a' && byte <= 'f')
01495                     byte = byte - 'a' + 10;
01496                 else if (byte >= '0' && byte <= '9')
01497                     byte = byte - '0';
01498                 else
01499                     break;
01500                 n = (n * 16) + byte;
01501             }
01502         }
01503         else {
01504             for (i = 1; i < 8; i++) {
01505                 byte = *(str + i);
01506                 if (byte >= '0' && byte <= '9')
01507                     n = (n * 10) + (byte - '0');
01508                 else
01509                     break;
01510             }
01511         }
01512         if (byte == ';') {
01513             str += i+1;
01514         }
01515         else {
01516             n = 0;
01517         }
01518     }
01519     else {
01520         key.name = p = entity_name_buf;
01521         for (i = 0; i <  ENTITY_NAME_LENGTH_MAX; i++) {
01522             byte = *(str + i);
01523             if (byte == '\0') break;
01524             if (byte == ';') {
01525                 *p++ = '\0';
01526                 res = bsearch(&key, entities, NR_OF_ENTITIES,
01527                     sizeof(entities[0]), *comp_entities);
01528                 if (res) {
01529                     n = res->value;
01530                     str += i+1;
01531                 }
01532                 break;
01533             }
01534             *p++ = byte;
01535         }
01536     }
01537     *s = (char*)str;
01538     return n;
01539 }
01540 
01541 static unsigned char
01542 cvtAndAppend (unsigned char c, agxbuf* xb)
01543 {
01544     char buf[2];
01545     char* s;
01546     char* p;
01547     int len;
01548 
01549     buf[0] = c;
01550     buf[1] = '\0';
01551 
01552     p = s = latin1ToUTF8 (buf);
01553     len = strlen(s);
01554     while (len-- > 1)
01555         agxbputc(xb, *p++);
01556     c = *p;
01557     free (s);
01558     return c;
01559 }
01560 
01561 /* htmlEntityUTF8:
01562  * substitute html entities like: &#123; and: &amp; with the UTF8 equivalents
01563  * check for invalid utf8. If found, treat a single byte as Latin-1, convert it to
01564  * utf8 and warn the user.
01565  */
01566 char* htmlEntityUTF8 (char* s, graph_t* g)
01567 {
01568     static graph_t* lastg;
01569     static boolean warned;
01570     char*  ns;
01571     agxbuf xb;
01572     unsigned char buf[BUFSIZ];
01573     unsigned char c;
01574     unsigned int v;
01575     int rc;
01576 
01577     if (lastg != g) {
01578         lastg = g;
01579         warned = 0;
01580     }
01581 
01582     agxbinit(&xb, BUFSIZ, buf);
01583 
01584     while ((c = *(unsigned char*)s++)) {
01585         if (c < 0xC0) {
01586             /*
01587              * Handles properly formed UTF-8 characters between
01588              * 0x01 and 0x7F.  Also treats \0 and naked trail
01589              * bytes 0x80 to 0xBF as valid characters representing
01590              * themselves.
01591              */
01592             if (c == '&') {
01593                 /* replace html entity sequences like: &amp;
01594                  * and: &#123; with their UTF8 equivalents */
01595                 v = htmlEntity (&s);
01596                 if (v) {
01597                     if (v < 0x7F) /* entity needs 1 byte in UTF8 */
01598                         c = v;
01599                     else if (v < 0x07FF) { /* entity needs 2 bytes in UTF8 */
01600                         rc = agxbputc(&xb, (v >> 6) | 0xC0);
01601                         c = (v & 0x3F) | 0x80;
01602                     }
01603                     else { /* entity needs 3 bytes in UTF8 */
01604                         rc = agxbputc(&xb, (v >> 12) | 0xE0);
01605                         rc = agxbputc(&xb, ((v >> 6) & 0x3F) | 0x80);
01606                         c = (v & 0x3F) | 0x80;
01607                     }
01608                 }
01609             }
01610         }
01611         else if (c < 0xE0) { /* copy 2 byte UTF8 characters */
01612             if ((*s & 0xC0) == 0x80) {
01613                 rc = agxbputc(&xb, c);
01614                 c = *(unsigned char*)s++;
01615             }
01616             else { 
01617                 if (!warned) {
01618                     agerr(AGWARN, "Invalid 2-byte UTF8 found in input of graph %s - treated as Latin-1. Perhaps \"-Gcharset=latin1\" is needed?\n", agnameof(g));
01619                     warned = 1;
01620                 }
01621                 c = cvtAndAppend (c, &xb);
01622             }
01623         }
01624         else if (c < 0xF0) { /* copy 3 byte UTF8 characters */
01625             if (((*s & 0xC0) == 0x80) && ((s[1] & 0xC0) == 0x80)) {
01626                 rc = agxbputc(&xb, c);
01627                 c = *(unsigned char*)s++;
01628                 rc = agxbputc(&xb, c);
01629                 c = *(unsigned char*)s++;
01630             }
01631             else {
01632                 if (!warned) {
01633                     agerr(AGWARN, "Invalid 3-byte UTF8 found in input of graph %s - treated as Latin-1. Perhaps \"-Gcharset=latin1\" is needed?\n", agnameof(g));
01634                     warned = 1;
01635                 }
01636                 c = cvtAndAppend (c, &xb);
01637             }
01638         }
01639         else  {
01640             if (!warned) {
01641                 agerr(AGWARN, "UTF8 codes > 3 bytes are not currently supported (graph %s) - treated as Latin-1. Perhaps \"-Gcharset=latin1\" is needed?\n", agnameof(g));
01642                 warned = 1;
01643             }
01644             c = cvtAndAppend (c, &xb);
01645         }
01646         rc = agxbputc(&xb, c);
01647     }
01648     ns = strdup (agxbuse(&xb));
01649     agxbfree(&xb);
01650     return ns;
01651 }
01652 
01653 /* latin1ToUTF8:
01654  * Converts string from Latin1 encoding to utf8
01655  * Also translates HTML entities.
01656  *
01657  */
01658 char* latin1ToUTF8 (char* s)
01659 {
01660     char*  ns;
01661     agxbuf xb;
01662     unsigned char buf[BUFSIZ];
01663     unsigned int  v;
01664     int rc;
01665     
01666     agxbinit(&xb, BUFSIZ, buf);
01667 
01668     /* Values are either a byte (<= 256) or come from htmlEntity, whose
01669      * values are all less than 0x07FF, so we need at most 3 bytes.
01670      */
01671     while ((v = *(unsigned char*)s++)) {
01672         if (v == '&') {
01673             v = htmlEntity (&s);
01674             if (!v) v = '&';
01675         }
01676         if (v < 0x7F)
01677             rc = agxbputc(&xb, v);
01678         else if (v < 0x07FF) {
01679             rc = agxbputc(&xb, (v >> 6) | 0xC0);
01680             rc = agxbputc(&xb, (v & 0x3F) | 0x80);
01681         }
01682         else {
01683             rc = agxbputc(&xb, (v >> 12) | 0xE0);
01684             rc = agxbputc(&xb, ((v >> 6) & 0x3F) | 0x80);
01685             rc = agxbputc(&xb, (v & 0x3F) | 0x80);
01686         }
01687     }
01688     ns = strdup (agxbuse(&xb));
01689     agxbfree(&xb);
01690     return ns;
01691 }
01692 
01693 /* utf8ToLatin1:
01694  * Converts string from utf8 encoding to Latin1
01695  * Note that it does not attempt to reproduce HTML entities.
01696  * We assume the input string comes from latin1ToUTF8.
01697  */
01698 char*
01699 utf8ToLatin1 (char* s)
01700 {
01701     char*  ns;
01702     agxbuf xb;
01703     unsigned char buf[BUFSIZ];
01704     unsigned char c;
01705     unsigned char outc;
01706     int rc;
01707     
01708     agxbinit(&xb, BUFSIZ, buf);
01709 
01710     while ((c = *(unsigned char*)s++)) {
01711         if (c < 0x7F)
01712             rc = agxbputc(&xb, c);
01713         else {
01714             outc = (c & 0x03) << 6;
01715             c = *(unsigned char*)s++;
01716             outc = outc | (c & 0x3F);
01717             rc = agxbputc(&xb, outc);
01718         }
01719     }
01720     ns = strdup (agxbuse(&xb));
01721     agxbfree(&xb);
01722     return ns;
01723 }
01724 
01725 boolean overlap_node(node_t *n, boxf b)
01726 {
01727     inside_t ictxt;
01728     pointf p;
01729 
01730     if (! OVERLAP(b, ND_bb(n)))
01731         return FALSE;
01732 
01733 /*  FIXME - need to do something better about CLOSEENOUGH */
01734     p = sub_pointf(ND_coord(n), mid_pointf(b.UR, b.LL));
01735 
01736     ictxt.s.n = n;
01737     ictxt.s.bp = NULL;
01738 
01739     return ND_shape(n)->fns->insidefn(&ictxt, p);
01740 }
01741 
01742 boolean overlap_label(textlabel_t *lp, boxf b)
01743 {
01744     pointf s;
01745     boxf bb;
01746 
01747     s.x = lp->dimen.x / 2.;
01748     s.y = lp->dimen.y / 2.;
01749     bb.LL = sub_pointf(lp->pos, s);
01750     bb.UR = add_pointf(lp->pos, s);
01751     return OVERLAP(b, bb);
01752 }
01753 
01754 static boolean overlap_arrow(pointf p, pointf u, double scale, int flag, boxf b)
01755 {
01756     if (OVERLAP(b, arrow_bb(p, u, scale, flag))) {
01757         /* FIXME - check inside arrow shape */
01758         return TRUE;
01759     }
01760     return FALSE;
01761 }
01762 
01763 static boolean overlap_bezier(bezier bz, boxf b)
01764 {
01765     int i;
01766     pointf p, u;
01767 
01768     assert(bz.size);
01769     u = bz.list[0];
01770     for (i = 1; i < bz.size; i++) {
01771         p = bz.list[i];
01772         if (lineToBox(p, u, b) != -1)
01773             return TRUE;
01774         u = p;
01775     }
01776 
01777     /* check arrows */
01778     if (bz.sflag) {
01779         if (overlap_arrow(bz.sp, bz.list[0], 1, bz.sflag, b))
01780             return TRUE;
01781     }
01782     if (bz.eflag) {
01783         if (overlap_arrow(bz.ep, bz.list[bz.size - 1], 1, bz.eflag, b))
01784             return TRUE;
01785     }
01786     return FALSE;
01787 }
01788 
01789 boolean overlap_edge(edge_t *e, boxf b)
01790 {
01791     int i;
01792     splines *spl;
01793     textlabel_t *lp;
01794 
01795     spl = ED_spl(e);
01796     if (spl && boxf_overlap(spl->bb, b))
01797         for (i = 0; i < spl->size; i++)
01798             if (overlap_bezier(spl->list[i], b))
01799                 return TRUE;
01800 
01801     lp = ED_label(e);
01802     if (lp && overlap_label(lp, b))
01803         return TRUE;
01804 
01805     return FALSE;
01806 }
01807 
01808 /* edgeType:
01809  * Convert string to edge type.
01810  */
01811 int edgeType (char* s, int dflt)
01812 {
01813     int et;
01814 
01815     if (!s || (*s == '\0')) return dflt;
01816 
01817     et = ET_NONE;
01818     switch (*s) {
01819     case '0' :    /* false */
01820         et = ET_LINE;
01821         break;
01822     case '1' :    /* true */
01823     case '2' :
01824     case '3' :
01825     case '4' :
01826     case '5' :
01827     case '6' :
01828     case '7' :
01829     case '8' :
01830     case '9' :
01831         et = ET_SPLINE;
01832         break;
01833     case 'c' :
01834     case 'C' :
01835         if (!strcasecmp (s+1, "ompound"))
01836             et = ET_COMPOUND;
01837         break;
01838     case 'f' :
01839     case 'F' :
01840         if (!strcasecmp (s+1, "alse"))
01841             et = ET_LINE;
01842         break;
01843     case 'l' :
01844     case 'L' :
01845         if (!strcasecmp (s+1, "ine"))
01846             et = ET_LINE;
01847         break;
01848     case 'n' :
01849     case 'N' :
01850         if (!strcasecmp (s+1, "one")) return et;
01851         if (!strcasecmp (s+1, "o")) return ET_LINE;
01852         break;
01853     case 'o' :
01854     case 'O' :
01855         if (!strcasecmp (s+1, "rtho"))
01856             et = ET_ORTHO;
01857         break;
01858     case 'p' :
01859     case 'P' :
01860         if (!strcasecmp (s+1, "olyline"))
01861             et = ET_PLINE;
01862         break;
01863     case 's' :
01864     case 'S' :
01865         if (!strcasecmp (s+1, "pline"))
01866             et = ET_SPLINE;
01867         break;
01868     case 't' :
01869     case 'T' :
01870         if (!strcasecmp (s+1, "rue"))
01871             et = ET_SPLINE;
01872         break;
01873     case 'y' :
01874     case 'Y' :
01875         if (!strcasecmp (s+1, "es"))
01876             et = ET_SPLINE;
01877         break;
01878     }
01879     if (!et) {
01880         agerr(AGWARN, "Unknown \"splines\" value: \"%s\" - ignored\n", s);
01881         et = dflt;
01882     }
01883     return et;
01884 }
01885 
01886 /* setEdgeType:
01887  * Sets graph's edge type based on the "splines" attribute.
01888  * If the attribute is not defined, use default.
01889  * If the attribute is "", use NONE.
01890  * If attribute value matches (case indepedent), use match.
01891  *   ortho => ET_ORTHO
01892  *   none => ET_NONE
01893  *   line => ET_LINE
01894  *   polyline => ET_PLINE
01895  *   spline => ET_SPLINE
01896  * If attribute is boolean, true means ET_SPLINE, false means ET_LINE.
01897  * Else warn and use default.
01898  */
01899 void setEdgeType (graph_t* g, int dflt)
01900 {
01901     char* s = agget(g, "splines");
01902     int et;
01903 
01904     if (!s) {
01905         et = dflt;
01906     }
01907     else if (*s == '\0') {
01908         et = ET_NONE;
01909     }
01910     else et = edgeType (s, dflt);
01911     GD_flags(g) |= et;
01912 }
01913 
01914 
01915 /* get_gradient_points
01916  * Evaluates the extreme points of an ellipse or polygon
01917  * Determines the point at the center of the extreme points
01918  * If isRadial is true,sets the inner radius to half the distance to the min point;
01919  * else uses the angle parameter to identify two points on a line that defines the 
01920  * gradient direction
01921  */
01922 void get_gradient_points(pointf * A, pointf * G, int n, float angle, boolean isRadial)
01923 {
01924     int i;
01925     double rx, ry;
01926     pointf min,max,center;
01927     
01928     if (n == 2) {
01929       rx = A[1].x - A[0].x;
01930       ry = A[1].y - A[0].y;
01931       min.x = A[0].x - rx;
01932       max.x = A[0].x + rx;
01933       min.y = A[0].y - ry;
01934       max.y = A[0].y + ry;
01935     }    
01936     else {
01937       min.x = max.x = A[0].x;
01938       min.y = max.y = A[0].y;
01939       for (i = 0; i < n; i++){
01940         min.x = MIN(A[i].x,min.x);
01941         min.y = MIN(A[i].y,min.y);
01942         max.x = MAX(A[i].x,max.x);
01943         max.y = MAX(A[i].y,max.y);
01944       }
01945     }
01946       center.x = min.x + (max.x - min.x)/2;
01947       center.y = min.y + (max.y - min.y)/2;
01948     if (isRadial) {
01949         double inner_r, outer_r;
01950       outer_r = sqrt((center.x - min.x)*(center.x - min.x) +
01951                       (center.y - min.y)*(center.y - min.y));
01952       inner_r = outer_r /4.;
01953       G[0].x = center.x;
01954       G[0].y = -center.y;
01955       G[1].x = inner_r;
01956       G[1].y = outer_r;
01957     }
01958     else {
01959       G[0].x = center.x - (max.x - center.x) * cos(angle);
01960       G[0].y = -center.y + (max.y - center.y) * sin(angle);
01961       G[1].x = center.x + (center.x - min.x) * cos(angle);
01962       G[1].y = -center.y - (center.y - min.y) * sin(angle);
01963     }
01964 }
01965 
01966 #ifndef WIN32_STATIC
01967 #ifndef HAVE_STRCASECMP
01968 
01969 
01970 #include <string.h>
01971 //#include <ctype.h>
01972 
01973 
01974 int strcasecmp(const char *s1, const char *s2)
01975 {
01976     while ((*s1 != '\0')
01977            && (tolower(*(unsigned char *) s1) ==
01978                tolower(*(unsigned char *) s2))) {
01979         s1++;
01980         s2++;
01981     }
01982 
01983     return tolower(*(unsigned char *) s1) - tolower(*(unsigned char *) s2);
01984 }
01985 
01986 #endif                          /* HAVE_STRCASECMP */
01987 #endif                          /* WIN32_STATIC */
01988 
01989 #ifndef WIN32_STATIC
01990 #ifndef HAVE_STRNCASECMP
01991 #include <string.h>
01992 //#include <ctype.h>
01993 
01994 int strncasecmp(const char *s1, const char *s2, unsigned int n)
01995 {
01996     if (n == 0)
01997         return 0;
01998 
01999     while ((n-- != 0)
02000            && (tolower(*(unsigned char *) s1) ==
02001                tolower(*(unsigned char *) s2))) {
02002         if (n == 0 || *s1 == '\0' || *s2 == '\0')
02003             return 0;
02004         s1++;
02005         s2++;
02006     }
02007 
02008     return tolower(*(unsigned char *) s1) - tolower(*(unsigned char *) s2);
02009 }
02010 
02011 #endif                          /* HAVE_STRNCASECMP */
02012 #endif                          /* WIN32_STATIC */
02013 void gv_free_splines(edge_t * e)
02014 {
02015     int i;
02016     if (ED_spl(e)) {
02017         for (i = 0; i < ED_spl(e)->size; i++)
02018             free(ED_spl(e)->list[i].list);
02019         free(ED_spl(e)->list);
02020         free(ED_spl(e));
02021     }
02022     ED_spl(e) = NULL;
02023 }
02024 
02025 void gv_cleanup_edge(edge_t * e)
02026 {
02027     free (ED_path(e).ps);
02028     gv_free_splines(e);
02029     free_label(ED_label(e));
02030     free_label(ED_xlabel(e));
02031     free_label(ED_head_label(e));
02032     free_label(ED_tail_label(e));
02033 #ifndef WITH_CGRAPH
02034     memset(&(e->u), 0, sizeof(Agedgeinfo_t));
02035 #else /* WITH_CGRAPH */
02036         /*FIX HERE , shallow cleaning may not be enough here */
02037         agdelrec(e, "Agedgeinfo_t");    
02038 #endif /* WITH_CGRAPH */
02039 }
02040 
02041 void gv_cleanup_node(node_t * n)
02042 {
02043     if (ND_pos(n)) free(ND_pos(n));
02044     if (ND_shape(n))
02045         ND_shape(n)->fns->freefn(n);
02046     free_label(ND_label(n));
02047     free_label(ND_xlabel(n));
02048 #ifndef WITH_CGRAPH
02049     memset(&(n->u), 0, sizeof(Agnodeinfo_t));
02050 #else /* WITH_CGRAPH */
02051         /*FIX HERE , shallow cleaning may not be enough here */
02052         agdelrec(n, "Agnodeinfo_t");    
02053 #endif /* WITH_CGRAPH */
02054 }
02055 
02056 void gv_nodesize(node_t * n, boolean flip)
02057 {
02058     double w;
02059 
02060     if (flip) {
02061         w = INCH2PS(ND_height(n));
02062         ND_lw(n) = ND_rw(n) = w / 2;
02063         ND_ht(n) = INCH2PS(ND_width(n));
02064     } 
02065     else {
02066         w = INCH2PS(ND_width(n));
02067         ND_lw(n) = ND_rw(n) = w / 2;
02068         ND_ht(n) = INCH2PS(ND_height(n));
02069     }
02070 }
02071 
02072 #ifdef WIN32    
02073 void fix_fc(void)
02074 {
02075     char buf[28192];
02076     char buf2[28192];
02077     int cur=0;
02078     FILE* fp;
02079 
02080     if((fp = fopen("fix-fc.exe", "r")) == NULL)
02081             return ;
02082     fclose (fp);
02083     if (!system ("fix-fc.exe")) {
02084         system ("del fix-fc.exe");
02085         system ("dot -c");      //run dot -c once too since we already run things :)
02086     }
02087 }
02088 #endif
02089 
02090 #ifndef HAVE_DRAND48
02091 double drand48(void)
02092 {
02093     double d;
02094     d = rand();
02095     d = d / RAND_MAX;
02096     return d;
02097 }
02098 #endif
02099 #ifdef WITH_CGRAPH
02100 typedef struct {
02101     Dtlink_t link;
02102     char* name;
02103     Agraph_t* clp;
02104 } clust_t;
02105 
02106 static void free_clust (Dt_t* dt, clust_t* clp, Dtdisc_t* disc)
02107 {
02108     free (clp);
02109 }
02110 
02111 static Dtdisc_t strDisc = {
02112     offsetof(clust_t,name),
02113     -1,
02114     offsetof(clust_t,link),
02115     NIL(Dtmake_f),
02116     (Dtfree_f)free_clust,
02117     NIL(Dtcompar_f),
02118     NIL(Dthash_f),
02119     NIL(Dtmemory_f),
02120     NIL(Dtevent_f)
02121 };
02122 
02123 static void fillMap (Agraph_t* g, Dt_t* map)
02124 {
02125     Agraph_t* cl;
02126     int c;
02127     char* s;
02128     clust_t* ip;
02129 
02130     for (c = 1; c <= GD_n_cluster(g); c++) {
02131         cl = GD_clust(g)[c];
02132         s = agnameof(cl);
02133         if (dtmatch (map, &s)) {
02134             agerr(AGWARN, "Two clusters named %s - the second will be ignored\n", s);
02135         }
02136         else {
02137             ip = NEW(clust_t);
02138             ip->name = s;
02139             ip->clp = cl;
02140             dtinsert (map, ip);
02141         }
02142         fillMap (cl, map);
02143     }
02144 }
02145 
02146 /* mkClustMap:
02147  * Generates a dictionary mapping cluster names to corresponding cluster.
02148  * Used with cgraph as the latter does not support a flat namespace of clusters.
02149  * Assumes G has already built a cluster tree using GD_n_cluster and GD_clust.
02150  */
02151 Dt_t* mkClustMap (Agraph_t* g)
02152 {
02153     Dt_t* map = dtopen (&strDisc, Dtoset);
02154 
02155     fillMap (g, map);
02156     
02157     return map;
02158 }
02159 
02160 Agraph_t*
02161 findCluster (Dt_t* map, char* name)
02162 {
02163     clust_t* clp = dtmatch (map, name);
02164     if (clp)
02165         return clp->clp;
02166     else
02167         return NULL;
02168 }
02169 #endif
02170 
02171 #ifdef WITH_CGRAPH
02172 #ifdef DEBUG
02173 Agnodeinfo_t* ninfo(Agnode_t* n) {return (Agnodeinfo_t*)AGDATA(n);}
02174 Agraphinfo_t* ginfo(Agraph_t* g) {return (Agraphinfo_t*)AGDATA(g);}
02175 Agedgeinfo_t* einfo(Agedge_t* e) {return (Agedgeinfo_t*)AGDATA(e);}
02176 #endif
02177 #endif