gv.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733
  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 "gv_channel.h"
  11. #include <cstdlib>
  12. #include <cstring>
  13. #include <gvc/gvc.h>
  14. #include <string>
  15. #include <util/alloc.h>
  16. #define agfindattr(x, s) agattrsym(x, s)
  17. #define agraphattr(g, n, s) agattr(g, AGRAPH, n, s)
  18. static char emptystring[] = {'\0'};
  19. static GVC_t *gvc;
  20. static void gv_init(void) {
  21. // list of builtins, enable demand loading
  22. gvc = gvContextPlugins(lt_preloaded_symbols, DEMAND_LOADING);
  23. }
  24. Agraph_t *graph(char *name) {
  25. if (!gvc)
  26. gv_init();
  27. return agopen(name, Agundirected, 0);
  28. }
  29. Agraph_t *digraph(char *name) {
  30. if (!gvc)
  31. gv_init();
  32. return agopen(name, Agdirected, 0);
  33. }
  34. Agraph_t *strictgraph(char *name) {
  35. if (!gvc)
  36. gv_init();
  37. return agopen(name, Agstrictundirected, 0);
  38. }
  39. Agraph_t *strictdigraph(char *name) {
  40. if (!gvc)
  41. gv_init();
  42. return agopen(name, Agstrictdirected, 0);
  43. }
  44. Agraph_t *readstring(char *string) {
  45. if (!gvc)
  46. gv_init();
  47. return agmemread(string);
  48. }
  49. Agraph_t *read(FILE *f) {
  50. if (!gvc)
  51. gv_init();
  52. return agread(f, nullptr);
  53. }
  54. Agraph_t *read(const char *filename) {
  55. FILE *f = fopen(filename, "r");
  56. if (!f)
  57. return nullptr;
  58. if (!gvc)
  59. gv_init();
  60. Agraph_t *g = agread(f, nullptr);
  61. fclose(f);
  62. return g;
  63. }
  64. //-------------------------------------------------
  65. Agraph_t *graph(Agraph_t *g, char *name) {
  66. if (!gvc)
  67. gv_init();
  68. return agsubg(g, name, 1);
  69. }
  70. Agnode_t *node(Agraph_t *g, char *name) {
  71. if (!gvc)
  72. return nullptr;
  73. return agnode(g, name, 1);
  74. }
  75. Agedge_t *edge(Agraph_t *g, Agnode_t *t, Agnode_t *h) {
  76. if (!gvc || !t || !h || !g)
  77. return nullptr;
  78. // edges from/to the protonode are not permitted
  79. if (AGTYPE(t) == AGRAPH || AGTYPE(h) == AGRAPH)
  80. return nullptr;
  81. return agedge(g, t, h, nullptr, 1);
  82. }
  83. Agedge_t *edge(Agnode_t *t, Agnode_t *h) { return edge(agraphof(t), t, h); }
  84. // induce tail if necessary
  85. Agedge_t *edge(char *tname, Agnode_t *h) {
  86. return edge(node(agraphof(h), tname), h);
  87. }
  88. // induce head if necessary
  89. Agedge_t *edge(Agnode_t *t, char *hname) {
  90. return edge(t, node(agraphof(t), hname));
  91. }
  92. // induce tail/head if necessary
  93. Agedge_t *edge(Agraph_t *g, char *tname, char *hname) {
  94. return edge(g, node(g, tname), node(g, hname));
  95. }
  96. //-------------------------------------------------
  97. static char *myagxget(void *obj, Agsym_t *a) {
  98. if (!obj || !a)
  99. return emptystring;
  100. char *val = agxget(obj, a);
  101. if (!val)
  102. return emptystring;
  103. if (strcmp(a->name, "label") == 0 && aghtmlstr(val)) {
  104. const std::string buf = std::string("<") + val + ">";
  105. return gv_strdup(buf.c_str());
  106. }
  107. return val;
  108. }
  109. char *getv(Agraph_t *g, Agsym_t *a) { return myagxget(g, a); }
  110. char *getv(Agraph_t *g, char *attr) {
  111. if (!g || !attr)
  112. return nullptr;
  113. Agsym_t *a = agfindattr(agroot(g), attr);
  114. return myagxget(g, a);
  115. }
  116. static void myagxset(void *obj, Agsym_t *a, char *val) {
  117. if (strcmp(a->name, "label") == 0 && val[0] == '<') {
  118. size_t len = strlen(val);
  119. if (val[len - 1] == '>') {
  120. std::string hs(val + 1, len - 2);
  121. val = agstrdup_html(agraphof(obj), hs.c_str());
  122. }
  123. }
  124. agxset(obj, a, val);
  125. }
  126. char *setv(Agraph_t *g, Agsym_t *a, char *val) {
  127. if (!g || !a || !val)
  128. return nullptr;
  129. myagxset(g, a, val);
  130. return val;
  131. }
  132. char *setv(Agraph_t *g, char *attr, char *val) {
  133. if (!g || !attr || !val)
  134. return nullptr;
  135. Agsym_t *a = agfindattr(agroot(g), attr);
  136. if (!a)
  137. a = agraphattr(g->root, attr, emptystring);
  138. myagxset(g, a, val);
  139. return val;
  140. }
  141. //-------------------------------------------------
  142. char *getv(Agnode_t *n, Agsym_t *a) {
  143. if (!n || !a)
  144. return nullptr;
  145. if (AGTYPE(n) == AGRAPH) // protonode
  146. return nullptr; // FIXME ??
  147. return myagxget(n, a);
  148. }
  149. char *getv(Agnode_t *n, char *attr) {
  150. if (!n || !attr)
  151. return nullptr;
  152. if (AGTYPE(n) == AGRAPH) // protonode
  153. return nullptr; // FIXME ??
  154. Agraph_t *g = agroot(agraphof(n));
  155. Agsym_t *a = agattr(g, AGNODE, attr, nullptr);
  156. return myagxget(n, a);
  157. }
  158. char *setv(Agnode_t *n, Agsym_t *a, char *val) {
  159. if (!n || !a || !val)
  160. return nullptr;
  161. if (AGTYPE(n) == AGRAPH) // protonode
  162. return nullptr; // FIXME ??
  163. myagxset(n, a, val);
  164. return val;
  165. }
  166. char *setv(Agnode_t *n, char *attr, char *val) {
  167. if (!n || !attr || !val)
  168. return nullptr;
  169. if (AGTYPE(n) == AGRAPH) { // protonode
  170. auto g = reinterpret_cast<Agraph_t *>(n);
  171. (void)agattr(g, AGNODE, attr,
  172. val); // create default attribute in pseudo protonode
  173. // FIXME? - deal with html in "label" attributes
  174. return val;
  175. }
  176. Agraph_t *g = agroot(agraphof(n));
  177. Agsym_t *a = agattr(g, AGNODE, attr, nullptr);
  178. if (!a)
  179. a = agattr(g, AGNODE, attr, emptystring);
  180. myagxset(n, a, val);
  181. return val;
  182. }
  183. //-------------------------------------------------
  184. char *getv(Agedge_t *e, Agsym_t *a) {
  185. if (!e || !a)
  186. return nullptr;
  187. if (AGTYPE(e) == AGRAPH) // protoedge
  188. return nullptr; // FIXME ??
  189. return myagxget(e, a);
  190. }
  191. char *getv(Agedge_t *e, char *attr) {
  192. if (!e || !attr)
  193. return nullptr;
  194. if (AGTYPE(e) == AGRAPH) // protoedge
  195. return nullptr; // FIXME ??
  196. Agraph_t *g = agraphof(agtail(e));
  197. Agsym_t *a = agattr(g, AGEDGE, attr, nullptr);
  198. return myagxget(e, a);
  199. }
  200. char *setv(Agedge_t *e, Agsym_t *a, char *val) {
  201. if (!e || !a || !val)
  202. return nullptr;
  203. if (AGTYPE(e) == AGRAPH) // protoedge
  204. return nullptr; // FIXME ??
  205. myagxset(e, a, val);
  206. return val;
  207. }
  208. char *setv(Agedge_t *e, char *attr, char *val) {
  209. if (!e || !attr || !val)
  210. return nullptr;
  211. if (AGTYPE(e) == AGRAPH) { // protoedge
  212. auto g = reinterpret_cast<Agraph_t *>(e);
  213. (void)agattr(g, AGEDGE, attr,
  214. val); // create default attribute in pseudo protoedge
  215. // FIXME? - deal with html in "label" attributes
  216. return val;
  217. }
  218. Agraph_t *g = agroot(agraphof(agtail(e)));
  219. Agsym_t *a = agattr(g, AGEDGE, attr, nullptr);
  220. if (!a)
  221. a = agattr(g, AGEDGE, attr, emptystring);
  222. myagxset(e, a, val);
  223. return val;
  224. }
  225. //-------------------------------------------------
  226. Agraph_t *findsubg(Agraph_t *g, char *name) {
  227. if (!g || !name)
  228. return nullptr;
  229. return agsubg(g, name, 0);
  230. }
  231. Agnode_t *findnode(Agraph_t *g, char *name) {
  232. if (!g || !name)
  233. return nullptr;
  234. return agnode(g, name, 0);
  235. }
  236. Agedge_t *findedge(Agnode_t *t, Agnode_t *h) {
  237. if (!t || !h)
  238. return nullptr;
  239. if (AGTYPE(t) == AGRAPH || AGTYPE(h) == AGRAPH)
  240. return nullptr;
  241. return agfindedge(agraphof(t), t, h);
  242. }
  243. Agsym_t *findattr(Agraph_t *g, char *name) {
  244. if (!g || !name)
  245. return nullptr;
  246. return agfindattr(g, name);
  247. }
  248. Agsym_t *findattr(Agnode_t *n, char *name) {
  249. if (!n || !name)
  250. return nullptr;
  251. return agfindattr(n, name);
  252. }
  253. Agsym_t *findattr(Agedge_t *e, char *name) {
  254. if (!e || !name)
  255. return nullptr;
  256. return agfindattr(e, name);
  257. }
  258. //-------------------------------------------------
  259. Agnode_t *headof(Agedge_t *e) {
  260. if (!e)
  261. return nullptr;
  262. if (AGTYPE(e) == AGRAPH)
  263. return nullptr;
  264. return aghead(e);
  265. }
  266. Agnode_t *tailof(Agedge_t *e) {
  267. if (!e)
  268. return nullptr;
  269. if (AGTYPE(e) == AGRAPH)
  270. return nullptr;
  271. return agtail(e);
  272. }
  273. Agraph_t *graphof(Agraph_t *g) {
  274. if (!g || g == g->root)
  275. return nullptr;
  276. return agroot(g);
  277. }
  278. Agraph_t *graphof(Agedge_t *e) {
  279. if (!e)
  280. return nullptr;
  281. if (AGTYPE(e) == AGRAPH)
  282. return reinterpret_cast<Agraph_t *>(
  283. e); // graph of protoedge is itself recast
  284. return agraphof(agtail(e));
  285. }
  286. Agraph_t *graphof(Agnode_t *n) {
  287. if (!n)
  288. return nullptr;
  289. if (AGTYPE(n) == AGRAPH)
  290. return reinterpret_cast<Agraph_t *>(
  291. n); // graph of protonode is itself recast
  292. return agraphof(n);
  293. }
  294. Agraph_t *rootof(Agraph_t *g) {
  295. if (!g)
  296. return nullptr;
  297. return agroot(g);
  298. }
  299. //-------------------------------------------------
  300. Agnode_t *protonode(Agraph_t *g) {
  301. return reinterpret_cast<Agnode_t *>(g); // gross abuse of the type system!
  302. }
  303. Agedge_t *protoedge(Agraph_t *g) {
  304. return reinterpret_cast<Agedge_t *>(g); // gross abuse of the type system!
  305. }
  306. //-------------------------------------------------
  307. char *nameof(Agraph_t *g) {
  308. if (!g)
  309. return nullptr;
  310. return agnameof(g);
  311. }
  312. char *nameof(Agnode_t *n) {
  313. if (!n)
  314. return nullptr;
  315. if (AGTYPE(n) == AGRAPH)
  316. return nullptr;
  317. return agnameof(n);
  318. }
  319. char *nameof(Agsym_t *a) {
  320. if (!a)
  321. return nullptr;
  322. return a->name;
  323. }
  324. //-------------------------------------------------
  325. bool ok(Agraph_t *g) { return g != nullptr; }
  326. bool ok(Agnode_t *n) { return n != nullptr; }
  327. bool ok(Agedge_t *e) { return e != nullptr; }
  328. bool ok(Agsym_t *a) { return a != nullptr; }
  329. //-------------------------------------------------
  330. Agraph_t *firstsubg(Agraph_t *g) {
  331. if (!g)
  332. return nullptr;
  333. return agfstsubg(g);
  334. }
  335. Agraph_t *nextsubg(Agraph_t *g, Agraph_t *sg) {
  336. if (!g || !sg)
  337. return nullptr;
  338. return agnxtsubg(sg);
  339. }
  340. Agraph_t *firstsupg(Agraph_t *g) { return g->parent; }
  341. Agraph_t *nextsupg(Agraph_t *, Agraph_t *) { return nullptr; }
  342. Agedge_t *firstout(Agraph_t *g) {
  343. if (!g)
  344. return nullptr;
  345. for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
  346. Agedge_t *e = agfstout(g, n);
  347. if (e)
  348. return e;
  349. }
  350. return nullptr;
  351. }
  352. Agedge_t *nextout(Agraph_t *g, Agedge_t *e) {
  353. if (!g || !e)
  354. return nullptr;
  355. Agedge_t *ne = agnxtout(g, e);
  356. if (ne)
  357. return ne;
  358. for (Agnode_t *n = agnxtnode(g, agtail(e)); n; n = agnxtnode(g, n)) {
  359. ne = agfstout(g, n);
  360. if (ne)
  361. return ne;
  362. }
  363. return nullptr;
  364. }
  365. Agedge_t *firstedge(Agraph_t *g) { return firstout(g); }
  366. Agedge_t *nextedge(Agraph_t *g, Agedge_t *e) { return nextout(g, e); }
  367. Agedge_t *firstout(Agnode_t *n) {
  368. if (!n)
  369. return nullptr;
  370. return agfstout(agraphof(n), n);
  371. }
  372. Agedge_t *nextout(Agnode_t *n, Agedge_t *e) {
  373. if (!n || !e)
  374. return nullptr;
  375. return agnxtout(agraphof(n), e);
  376. }
  377. Agnode_t *firsthead(Agnode_t *n) {
  378. if (!n)
  379. return nullptr;
  380. Agedge_t *e = agfstout(agraphof(n), n);
  381. if (!e)
  382. return nullptr;
  383. return aghead(e);
  384. }
  385. Agnode_t *nexthead(Agnode_t *n, Agnode_t *h) {
  386. if (!n || !h)
  387. return nullptr;
  388. Agraph_t *g = agraphof(n);
  389. Agedge_t *e = agfindedge(g, n, h);
  390. if (!e)
  391. return nullptr;
  392. do {
  393. e = agnxtout(g, AGMKOUT(e));
  394. if (!e)
  395. return nullptr;
  396. } while (aghead(e) == h);
  397. return aghead(e);
  398. }
  399. Agedge_t *firstedge(Agnode_t *n) {
  400. if (!n)
  401. return nullptr;
  402. return agfstedge(agraphof(n), n);
  403. }
  404. Agedge_t *nextedge(Agnode_t *n, Agedge_t *e) {
  405. if (!n || !e)
  406. return nullptr;
  407. return agnxtedge(agraphof(n), e, n);
  408. }
  409. Agedge_t *firstin(Agraph_t *g) {
  410. if (!g)
  411. return nullptr;
  412. Agnode_t *n = agfstnode(g);
  413. if (!n)
  414. return nullptr;
  415. return agfstin(g, n);
  416. }
  417. Agedge_t *nextin(Agraph_t *g, Agedge_t *e) {
  418. if (!g || !e)
  419. return nullptr;
  420. Agedge_t *ne = agnxtin(g, e);
  421. if (ne)
  422. return ne;
  423. Agnode_t *n = agnxtnode(g, aghead(e));
  424. if (!n)
  425. return nullptr;
  426. return agfstin(g, n);
  427. }
  428. Agedge_t *firstin(Agnode_t *n) {
  429. if (!n)
  430. return nullptr;
  431. return agfstin(agraphof(n), n);
  432. }
  433. Agedge_t *nextin(Agnode_t *n, Agedge_t *e) {
  434. if (!n || !e)
  435. return nullptr;
  436. return agnxtin(agraphof(n), e);
  437. }
  438. Agnode_t *firsttail(Agnode_t *n) {
  439. if (!n)
  440. return nullptr;
  441. Agedge_t *e = agfstin(agraphof(n), n);
  442. if (!e)
  443. return nullptr;
  444. return agtail(e);
  445. }
  446. Agnode_t *nexttail(Agnode_t *n, Agnode_t *t) {
  447. if (!n || !t)
  448. return nullptr;
  449. Agraph_t *g = agraphof(n);
  450. Agedge_t *e = agfindedge(g, t, n);
  451. if (!e)
  452. return nullptr;
  453. do {
  454. e = agnxtin(g, AGMKIN(e));
  455. if (!e)
  456. return nullptr;
  457. } while (agtail(e) == t);
  458. return agtail(e);
  459. }
  460. Agnode_t *firstnode(Agraph_t *g) {
  461. if (!g)
  462. return nullptr;
  463. return agfstnode(g);
  464. }
  465. Agnode_t *nextnode(Agraph_t *g, Agnode_t *n) {
  466. if (!g || !n)
  467. return nullptr;
  468. return agnxtnode(g, n);
  469. }
  470. Agnode_t *firstnode(Agedge_t *e) {
  471. if (!e)
  472. return nullptr;
  473. return agtail(e);
  474. }
  475. Agnode_t *nextnode(Agedge_t *e, Agnode_t *n) {
  476. if (!e || n != agtail(e))
  477. return nullptr;
  478. return aghead(e);
  479. }
  480. Agsym_t *firstattr(Agraph_t *g) {
  481. if (!g)
  482. return nullptr;
  483. g = agroot(g);
  484. return agnxtattr(g, AGRAPH, nullptr);
  485. }
  486. Agsym_t *nextattr(Agraph_t *g, Agsym_t *a) {
  487. if (!g || !a)
  488. return nullptr;
  489. g = agroot(g);
  490. return agnxtattr(g, AGRAPH, a);
  491. }
  492. Agsym_t *firstattr(Agnode_t *n) {
  493. if (!n)
  494. return nullptr;
  495. Agraph_t *g = agraphof(n);
  496. return agnxtattr(g, AGNODE, nullptr);
  497. }
  498. Agsym_t *nextattr(Agnode_t *n, Agsym_t *a) {
  499. if (!n || !a)
  500. return nullptr;
  501. Agraph_t *g = agraphof(n);
  502. return agnxtattr(g, AGNODE, a);
  503. }
  504. Agsym_t *firstattr(Agedge_t *e) {
  505. if (!e)
  506. return nullptr;
  507. Agraph_t *g = agraphof(agtail(e));
  508. return agnxtattr(g, AGEDGE, nullptr);
  509. }
  510. Agsym_t *nextattr(Agedge_t *e, Agsym_t *a) {
  511. if (!e || !a)
  512. return nullptr;
  513. Agraph_t *g = agraphof(agtail(e));
  514. return agnxtattr(g, AGEDGE, a);
  515. }
  516. bool rm(Agraph_t *g) {
  517. if (!g)
  518. return false;
  519. // The rm function appears to have the semantics of agclose, so
  520. // we should just do that, and let cgraph take care of all the
  521. // details.
  522. agclose(g);
  523. return true;
  524. }
  525. bool rm(Agnode_t *n) {
  526. if (!n)
  527. return false;
  528. // removal of the protonode is not permitted
  529. if (strcmp(agnameof(n), "\001proto") == 0)
  530. return false;
  531. agdelete(agraphof(n), n);
  532. return true;
  533. }
  534. bool rm(Agedge_t *e) {
  535. if (!e)
  536. return false;
  537. // removal of the protoedge is not permitted
  538. if (strcmp(agnameof(aghead(e)), "\001proto") == 0 ||
  539. strcmp(agnameof(agtail(e)), "\001proto") == 0)
  540. return false;
  541. agdelete(agroot(agraphof(aghead(e))), e);
  542. return true;
  543. }
  544. bool layout(Agraph_t *g, const char *engine) {
  545. if (!g)
  546. return false;
  547. (void)gvFreeLayout(gvc, g); // ignore errors
  548. int err = gvLayout(gvc, g, engine);
  549. return err == 0;
  550. }
  551. // annotate the graph with layout information
  552. bool render(Agraph_t *g) {
  553. if (!g)
  554. return false;
  555. attach_attrs(g);
  556. return true;
  557. }
  558. // render to stdout
  559. bool render(Agraph_t *g, const char *format) {
  560. if (!g)
  561. return false;
  562. int err = gvRender(gvc, g, format, stdout);
  563. return err == 0;
  564. }
  565. // render to an open FILE
  566. bool render(Agraph_t *g, const char *format, FILE *f) {
  567. if (!g)
  568. return false;
  569. int err = gvRender(gvc, g, format, f);
  570. return err == 0;
  571. }
  572. // render to an open channel
  573. bool renderchannel(Agraph_t *g, const char *format, const char *channelname) {
  574. if (!g)
  575. return false;
  576. gv_channel_writer_init(gvc);
  577. int err = gvRender(gvc, g, format, (FILE *)channelname);
  578. gv_writer_reset(gvc); // Reset to default
  579. return err == 0;
  580. }
  581. // render to a filename
  582. bool render(Agraph_t *g, const char *format, const char *filename) {
  583. if (!g)
  584. return false;
  585. int err = gvRenderFilename(gvc, g, format, filename);
  586. return err == 0;
  587. }
  588. typedef struct {
  589. char *data;
  590. int sz; // buffer size
  591. int len; // length of array
  592. } BA;
  593. // render to string result, using binding-dependent gv_string_writer()
  594. char *renderresult(Agraph_t *g, const char *format) {
  595. if (!g)
  596. return nullptr;
  597. if (!GD_alg(g))
  598. return nullptr;
  599. BA ba;
  600. ba.sz = BUFSIZ;
  601. // must be freed by wrapper code
  602. ba.data = reinterpret_cast<char *>(gv_calloc(ba.sz, sizeof(char)));
  603. ba.len = 0;
  604. gv_string_writer_init(gvc);
  605. (void)gvRender(gvc, g, format, reinterpret_cast<FILE *>(&ba));
  606. gv_writer_reset(gvc); // Reset to default
  607. *reinterpret_cast<int *>(GD_alg(g)) = ba.len;
  608. return ba.data;
  609. }
  610. // render to string result, using binding-dependent gv_string_writer()
  611. void renderresult(Agraph_t *g, const char *format, char *outdata) {
  612. if (!g)
  613. return;
  614. gv_string_writer_init(gvc);
  615. (void)gvRender(gvc, g, format, reinterpret_cast<FILE *>(outdata));
  616. gv_writer_reset(gvc); // Reset to default
  617. }
  618. // render to a malloc'ed data string, to be free'd by caller.
  619. char *renderdata(Agraph_t *g, const char *format) {
  620. if (!g)
  621. return nullptr;
  622. char *data;
  623. unsigned int length;
  624. int err = gvRenderData(gvc, g, format, &data, &length);
  625. if (err)
  626. return nullptr;
  627. return data;
  628. }
  629. bool write(Agraph_t *g, FILE *f) {
  630. if (!g)
  631. return false;
  632. int err = agwrite(g, f);
  633. return err == 0;
  634. }
  635. bool write(Agraph_t *g, const char *filename) {
  636. if (!g)
  637. return false;
  638. FILE *f = fopen(filename, "w");
  639. if (!f)
  640. return false;
  641. int err = agwrite(g, f);
  642. fclose(f);
  643. return err == 0;
  644. }
  645. bool tred(Agraph_t *g) {
  646. if (!g)
  647. return false;
  648. int err = gvToolTred(g);
  649. return err == 0;
  650. }