|
Graphviz
2.29.20120524.0446
|
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: { and: & 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: & 01594 * and: { 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
1.7.5