tcldot-util.c 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. /*************************************************************************
  2. * Copyright (c) 2011 AT&T Intellectual Property
  3. * All rights reserved. This program and the accompanying materials
  4. * are made available under the terms of the Eclipse Public License v1.0
  5. * which accompanies this distribution, and is available at
  6. * https://www.eclipse.org/legal/epl-v10.html
  7. *
  8. * Contributors: Details at https://graphviz.org
  9. *************************************************************************/
  10. #include <limits.h>
  11. #include <math.h>
  12. #include <stddef.h>
  13. #include "tcldot.h"
  14. #include <gvc/gvc.h>
  15. #include <util/alloc.h>
  16. #include <util/strcasecmp.h>
  17. #include <util/unreachable.h>
  18. size_t Tcldot_string_writer(GVJ_t *job, const char *s, size_t len)
  19. {
  20. tcldot_context_t *context = job->context;
  21. Tcl_AppendResult(context->interp, s, NULL);
  22. return len;
  23. }
  24. size_t Tcldot_channel_writer(GVJ_t *job, const char *s, size_t len)
  25. {
  26. if (len > INT_MAX) {
  27. len = INT_MAX;
  28. }
  29. const int written = Tcl_Write((Tcl_Channel)(job->output_file), s, (int)len);
  30. if (written < 0) {
  31. return 0;
  32. }
  33. return (size_t)written;
  34. }
  35. /* handles (tcl commands) to obj* */
  36. Agraph_t *cmd2g(const char *cmd) {
  37. Agraph_t *g = NULL;
  38. if (sscanf(cmd, "graph%p", &g) != 1 || !g)
  39. return NULL;
  40. return g;
  41. }
  42. Agnode_t *cmd2n(const char *cmd) {
  43. Agnode_t *n = NULL;
  44. if (sscanf(cmd, "node%p", &n) != 1 || !n)
  45. return NULL;
  46. return n;
  47. }
  48. Agedge_t *cmd2e(const char *cmd) {
  49. Agedge_t *e = NULL;
  50. if (sscanf(cmd, "edge%p", &e) != 1 || !e)
  51. return NULL;
  52. return e;
  53. }
  54. /* obj* to handles (tcl commands) */
  55. char *obj2cmd (void *obj) {
  56. static char buf[32];
  57. switch (AGTYPE(obj)) {
  58. case AGRAPH: snprintf(buf, sizeof(buf), "graph%p", obj); break;
  59. case AGNODE: snprintf(buf, sizeof(buf), "node%p", obj); break;
  60. case AGINEDGE:
  61. case AGOUTEDGE: snprintf(buf, sizeof(buf), "edge%p", obj); break;
  62. default: UNREACHABLE();
  63. }
  64. return buf;
  65. }
  66. void deleteEdge(gctx_t *gctx, Agraph_t * g, Agedge_t *e)
  67. {
  68. (void)g;
  69. char *hndl;
  70. hndl = obj2cmd(e);
  71. agdelete(gctx->g, e); /* delete edge from root graph */
  72. Tcl_DeleteCommand(gctx->ictx->interp, hndl);
  73. }
  74. static void deleteNodeEdges(gctx_t *gctx, Agraph_t *g, Agnode_t *n)
  75. {
  76. Agedge_t *e, *e1;
  77. e = agfstedge(g, n);
  78. while (e) {
  79. e1 = agnxtedge(g, e, n);
  80. deleteEdge(gctx, g, e);
  81. e = e1;
  82. }
  83. }
  84. void deleteNode(gctx_t * gctx, Agraph_t *g, Agnode_t *n)
  85. {
  86. (void)g;
  87. char *hndl;
  88. deleteNodeEdges(gctx, gctx->g, n); /* delete all edges to/from node in root graph */
  89. hndl = obj2cmd(n);
  90. agdelete(gctx->g, n); /* delete node from root graph */
  91. Tcl_DeleteCommand(gctx->ictx->interp, hndl);
  92. }
  93. static void deleteGraphNodes(gctx_t * gctx, Agraph_t *g)
  94. {
  95. Agnode_t *n, *n1;
  96. n = agfstnode(g);
  97. while (n) {
  98. n1 = agnxtnode(g, n);
  99. deleteNode(gctx, g, n);
  100. n = n1;
  101. }
  102. }
  103. void deleteGraph(gctx_t * gctx, Agraph_t *g)
  104. {
  105. Agraph_t *sg;
  106. char *hndl;
  107. for (sg = agfstsubg (g); sg; sg = agnxtsubg (sg)) {
  108. deleteGraph(gctx, sg);
  109. }
  110. deleteGraphNodes(gctx, g);
  111. hndl = obj2cmd(g);
  112. if (g == agroot(g)) {
  113. agclose(g);
  114. } else {
  115. agdelsubg(agroot(g), g);
  116. }
  117. Tcl_DeleteCommand(gctx->ictx->interp, hndl);
  118. }
  119. static void myagxset(void *obj, Agsym_t *a, char *val)
  120. {
  121. char *hs;
  122. if (strcmp(a->name, "label") == 0 && val[0] == '<') {
  123. size_t len = strlen(val);
  124. if (val[len-1] == '>') {
  125. hs = strdup(val+1);
  126. *(hs+len-2) = '\0';
  127. val = agstrdup_html(agraphof(obj),hs);
  128. free(hs);
  129. }
  130. }
  131. agxset(obj, a, val);
  132. }
  133. void setgraphattributes(Agraph_t * g, char *argv[], int argc)
  134. {
  135. int i;
  136. Agsym_t *a;
  137. for (i = 0; i < argc; i++) {
  138. if (!(a = agfindgraphattr(agroot(g), argv[i])))
  139. a = agattr(agroot(g), AGRAPH, argv[i], "");
  140. myagxset(g, a, argv[++i]);
  141. }
  142. }
  143. void setedgeattributes(Agraph_t * g, Agedge_t * e, char *argv[], int argc)
  144. {
  145. int i;
  146. Agsym_t *a;
  147. for (i = 0; i < argc; i++) {
  148. /* silently ignore attempts to modify "key" */
  149. if (strcmp(argv[i], "key") == 0) {
  150. i++;
  151. continue;
  152. }
  153. if (e) {
  154. if (!(a = agfindedgeattr(g, argv[i])))
  155. a = agattr(agroot(g), AGEDGE, argv[i], "");
  156. myagxset(e, a, argv[++i]);
  157. }
  158. else {
  159. agattr(g, AGEDGE, argv[i], argv[i+1]);
  160. i++;
  161. }
  162. }
  163. }
  164. void setnodeattributes(Agraph_t * g, Agnode_t * n, char *argv[], int argc)
  165. {
  166. int i;
  167. Agsym_t *a;
  168. for (i = 0; i < argc; i++) {
  169. if (n) {
  170. if (!(a = agfindnodeattr(g, argv[i])))
  171. a = agattr(agroot(g), AGNODE, argv[i], "");
  172. myagxset(n, a, argv[++i]);
  173. }
  174. else {
  175. agattr(g, AGNODE, argv[i], argv[i+1]);
  176. i++;
  177. }
  178. }
  179. }
  180. void listGraphAttrs (Tcl_Interp * interp, Agraph_t* g)
  181. {
  182. Agsym_t *a = NULL;
  183. while ((a = agnxtattr(g, AGRAPH, a))) {
  184. Tcl_AppendElement(interp, a->name);
  185. }
  186. }
  187. void listNodeAttrs (Tcl_Interp * interp, Agraph_t* g)
  188. {
  189. Agsym_t *a = NULL;
  190. while ((a = agnxtattr(g, AGNODE, a))) {
  191. Tcl_AppendElement(interp, a->name);
  192. }
  193. }
  194. void listEdgeAttrs (Tcl_Interp * interp, Agraph_t* g)
  195. {
  196. Agsym_t *a = NULL;
  197. while ((a = agnxtattr(g, AGEDGE, a))) {
  198. Tcl_AppendElement(interp, a->name);
  199. }
  200. }
  201. void tcldot_layout(GVC_t *gvc, Agraph_t * g, const char *engine)
  202. {
  203. gvFreeLayout(gvc, g); /* in case previously drawn */
  204. /* support old behaviors if engine isn't specified*/
  205. if (!engine || *engine == '\0') {
  206. if (agisdirected(g))
  207. engine = "dot";
  208. else
  209. engine = "neato";
  210. }
  211. else {
  212. if (strcasecmp(engine, "nop") == 0) {
  213. Nop = 2;
  214. PSinputscale = POINTS_PER_INCH;
  215. engine = "neato";
  216. }
  217. }
  218. gvLayout(gvc, g, engine);
  219. }
  220. char **tcldot_argv_dup(int argc, const char *argv[]) {
  221. assert(argc > 0);
  222. char **argv_ret = gv_calloc((size_t)argc, sizeof(char *));
  223. for (int i = 0; i < argc; ++i) {
  224. argv_ret[i] = gv_strdup(argv[i]);
  225. }
  226. return argv_ret;
  227. }
  228. void tcldot_argv_free(int argc, char *argv[]) {
  229. for (int i = 0; i < argc; ++i) {
  230. free(argv[i]);
  231. }
  232. free(argv);
  233. }