xdot.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998
  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 <cgraph/gv_ctype.h>
  11. #include <xdot/xdot.h>
  12. #include <stdlib.h>
  13. #include <string.h>
  14. #include <util/agxbuf.h>
  15. #include <util/alloc.h>
  16. #include <util/prisize_t.h>
  17. #include <util/unreachable.h>
  18. /* the parse functions should return NULL on error */
  19. static char *parseReal(char *s, double *fp)
  20. {
  21. char *p;
  22. double d;
  23. d = strtod(s, &p);
  24. if (p == s) return 0;
  25. *fp = d;
  26. return (p);
  27. }
  28. static char *parseInt(char *s, int *ip)
  29. {
  30. char* endp;
  31. *ip = (int)strtol (s, &endp, 10);
  32. if (s == endp)
  33. return 0;
  34. else
  35. return endp;
  36. }
  37. static char *parseUInt(char *s, unsigned int *ip)
  38. {
  39. char* endp;
  40. *ip = (unsigned int)strtoul (s, &endp, 10);
  41. if (s == endp)
  42. return 0;
  43. else
  44. return endp;
  45. }
  46. static char *parseRect(char *s, xdot_rect * rp)
  47. {
  48. char* endp;
  49. rp->x = strtod (s, &endp);
  50. if (s == endp)
  51. return 0;
  52. else
  53. s = endp;
  54. rp->y = strtod (s, &endp);
  55. if (s == endp)
  56. return 0;
  57. else
  58. s = endp;
  59. rp->w = strtod (s, &endp);
  60. if (s == endp)
  61. return 0;
  62. else
  63. s = endp;
  64. rp->h = strtod (s, &endp);
  65. if (s == endp)
  66. return 0;
  67. else
  68. s = endp;
  69. return s;
  70. }
  71. static char *parsePolyline(char *s, xdot_polyline * pp)
  72. {
  73. unsigned i;
  74. xdot_point *pts;
  75. xdot_point *ps;
  76. char* endp;
  77. s = parseUInt(s, &i);
  78. if (!s) return NULL;
  79. pts = ps = gv_calloc(i, sizeof(ps[0]));
  80. pp->cnt = i;
  81. for (i = 0; i < pp->cnt; i++) {
  82. ps->x = strtod (s, &endp);
  83. if (s == endp) {
  84. free (pts);
  85. return NULL;
  86. }
  87. else
  88. s = endp;
  89. ps->y = strtod (s, &endp);
  90. if (s == endp) {
  91. free (pts);
  92. return NULL;
  93. }
  94. else
  95. s = endp;
  96. ps->z = 0;
  97. ps++;
  98. }
  99. pp->pts = pts;
  100. return s;
  101. }
  102. static char *parseString(char *s, char **sp)
  103. {
  104. int i;
  105. s = parseInt(s, &i);
  106. if (!s || i <= 0) return 0;
  107. while (*s && *s != '-') s++;
  108. if (*s) s++;
  109. else {
  110. return 0;
  111. }
  112. // The leading number we just read indicates the count of originating
  113. // characters in the upcoming string. But the string may contain \-escaped
  114. // characters. So the count alone does not tell us how many bytes we need to
  115. // now read.
  116. agxbuf c = {0};
  117. int j = 0;
  118. for (int accounted = 0; accounted < i; ++j) {
  119. if (s[j] == '\0') {
  120. agxbfree(&c);
  121. return 0;
  122. }
  123. agxbputc(&c, s[j]);
  124. // only count this character if it was not an escape prefix
  125. if (s[j] != '\\' || (j > 0 && s[j - 1] == '\\')) {
  126. ++accounted;
  127. }
  128. }
  129. *sp = agxbdisown(&c);
  130. return s + j;
  131. }
  132. static char *parseAlign(char *s, xdot_align * ap)
  133. {
  134. int i;
  135. s = parseInt(s, &i);
  136. if (i < 0)
  137. *ap = xd_left;
  138. else if (i > 0)
  139. *ap = xd_right;
  140. else
  141. *ap = xd_center;
  142. return s;
  143. }
  144. #define CHK(s) if(!s){*error=1;return 0;}
  145. static char *parseOp(xdot_op * op, char *s, drawfunc_t ops[], int* error)
  146. {
  147. char* cs;
  148. xdot_color clr;
  149. *error = 0;
  150. while (gv_isspace(*s))
  151. s++;
  152. switch (*s++) {
  153. case 'E':
  154. op->kind = xd_filled_ellipse;
  155. s = parseRect(s, &op->u.ellipse);
  156. CHK(s);
  157. if (ops)
  158. op->drawfunc = ops[xop_ellipse];
  159. break;
  160. case 'e':
  161. op->kind = xd_unfilled_ellipse;
  162. s = parseRect(s, &op->u.ellipse);
  163. CHK(s);
  164. if (ops)
  165. op->drawfunc = ops[xop_ellipse];
  166. break;
  167. case 'P':
  168. op->kind = xd_filled_polygon;
  169. s = parsePolyline(s, &op->u.polygon);
  170. CHK(s);
  171. if (ops)
  172. op->drawfunc = ops[xop_polygon];
  173. break;
  174. case 'p':
  175. op->kind = xd_unfilled_polygon;
  176. s = parsePolyline(s, &op->u.polygon);
  177. CHK(s);
  178. if (ops)
  179. op->drawfunc = ops[xop_polygon];
  180. break;
  181. case 'b':
  182. op->kind = xd_filled_bezier;
  183. s = parsePolyline(s, &op->u.bezier);
  184. CHK(s);
  185. if (ops)
  186. op->drawfunc = ops[xop_bezier];
  187. break;
  188. case 'B':
  189. op->kind = xd_unfilled_bezier;
  190. s = parsePolyline(s, &op->u.bezier);
  191. CHK(s);
  192. if (ops)
  193. op->drawfunc = ops[xop_bezier];
  194. break;
  195. case 'c':
  196. s = parseString(s, &cs);
  197. CHK(s);
  198. cs = parseXDotColor (cs, &clr);
  199. CHK(cs);
  200. if (clr.type == xd_none) {
  201. op->kind = xd_pen_color;
  202. op->u.color = clr.u.clr;
  203. if (ops)
  204. op->drawfunc = ops[xop_pen_color];
  205. }
  206. else {
  207. op->kind = xd_grad_pen_color;
  208. op->u.grad_color = clr;
  209. if (ops)
  210. op->drawfunc = ops[xop_grad_color];
  211. }
  212. break;
  213. case 'C':
  214. s = parseString(s, &cs);
  215. CHK(s);
  216. cs = parseXDotColor (cs, &clr);
  217. CHK(cs);
  218. if (clr.type == xd_none) {
  219. op->kind = xd_fill_color;
  220. op->u.color = clr.u.clr;
  221. if (ops)
  222. op->drawfunc = ops[xop_fill_color];
  223. }
  224. else {
  225. op->kind = xd_grad_fill_color;
  226. op->u.grad_color = clr;
  227. if (ops)
  228. op->drawfunc = ops[xop_grad_color];
  229. }
  230. break;
  231. case 'L':
  232. op->kind = xd_polyline;
  233. s = parsePolyline(s, &op->u.polyline);
  234. CHK(s);
  235. if (ops)
  236. op->drawfunc = ops[xop_polyline];
  237. break;
  238. case 'T':
  239. op->kind = xd_text;
  240. s = parseReal(s, &op->u.text.x);
  241. CHK(s);
  242. s = parseReal(s, &op->u.text.y);
  243. CHK(s);
  244. s = parseAlign(s, &op->u.text.align);
  245. CHK(s);
  246. s = parseReal(s, &op->u.text.width);
  247. CHK(s);
  248. s = parseString(s, &op->u.text.text);
  249. CHK(s);
  250. if (ops)
  251. op->drawfunc = ops[xop_text];
  252. break;
  253. case 'F':
  254. op->kind = xd_font;
  255. s = parseReal(s, &op->u.font.size);
  256. CHK(s);
  257. s = parseString(s, &op->u.font.name);
  258. CHK(s);
  259. if (ops)
  260. op->drawfunc = ops[xop_font];
  261. break;
  262. case 'S':
  263. op->kind = xd_style;
  264. s = parseString(s, &op->u.style);
  265. CHK(s);
  266. if (ops)
  267. op->drawfunc = ops[xop_style];
  268. break;
  269. case 'I':
  270. op->kind = xd_image;
  271. s = parseRect(s, &op->u.image.pos);
  272. CHK(s);
  273. s = parseString(s, &op->u.image.name);
  274. CHK(s);
  275. if (ops)
  276. op->drawfunc = ops[xop_image];
  277. break;
  278. case 't':
  279. op->kind = xd_fontchar;
  280. s = parseUInt(s, &op->u.fontchar);
  281. CHK(s);
  282. if (ops)
  283. op->drawfunc = ops[xop_fontchar];
  284. break;
  285. case '\0':
  286. s = 0;
  287. break;
  288. default:
  289. *error = 1;
  290. s = 0;
  291. break;
  292. }
  293. return s;
  294. }
  295. #define XDBSIZE 100
  296. /* parseXDotFOn:
  297. * Parse and append additional xops onto a given xdot object.
  298. * Return x.
  299. */
  300. xdot *parseXDotFOn(char *s, drawfunc_t fns[], size_t sz, xdot *x) {
  301. xdot_op op;
  302. char *ops;
  303. size_t oldsz, bufsz;
  304. int error;
  305. if (!s)
  306. return x;
  307. if (!x) {
  308. x = gv_alloc(sizeof(*x));
  309. if (sz <= sizeof(xdot_op))
  310. sz = sizeof(xdot_op);
  311. /* cnt, freefunc, ops, flags zeroed by gv_alloc */
  312. x->sz = sz;
  313. }
  314. size_t initcnt = x->cnt;
  315. sz = x->sz;
  316. if (initcnt == 0) {
  317. bufsz = XDBSIZE;
  318. ops = gv_calloc(XDBSIZE, sz);
  319. }
  320. else {
  321. bufsz = initcnt + XDBSIZE;
  322. ops = gv_recalloc(x->ops, initcnt, bufsz, sz);
  323. }
  324. while ((s = parseOp(&op, s, fns, &error))) {
  325. if (x->cnt == bufsz) {
  326. oldsz = bufsz;
  327. bufsz *= 2;
  328. ops = gv_recalloc(ops, oldsz, bufsz, sz);
  329. }
  330. *(xdot_op *) (ops + x->cnt * sz) = op;
  331. x->cnt++;
  332. }
  333. if (error)
  334. x->flags |= XDOT_PARSE_ERROR;
  335. if (x->cnt) {
  336. x->ops = gv_recalloc(ops, bufsz, x->cnt, sz);
  337. }
  338. else {
  339. free (ops);
  340. free (x);
  341. x = NULL;
  342. }
  343. return x;
  344. }
  345. xdot *parseXDotF(char *s, drawfunc_t fns[], size_t sz) {
  346. return parseXDotFOn (s, fns, sz, NULL);
  347. }
  348. xdot *parseXDot(char *s)
  349. {
  350. return parseXDotF(s, 0, 0);
  351. }
  352. typedef int (*pf)(void*, char*, ...);
  353. static void printRect(xdot_rect * r, pf print, void *info)
  354. {
  355. agxbuf buf = {0};
  356. agxbprint(&buf, " %.02f", r->x);
  357. agxbuf_trim_zeros(&buf);
  358. print(info, "%s", agxbuse(&buf));
  359. agxbprint(&buf, " %.02f", r->y);
  360. agxbuf_trim_zeros(&buf);
  361. print(info, "%s", agxbuse(&buf));
  362. agxbprint(&buf, " %.02f", r->w);
  363. agxbuf_trim_zeros(&buf);
  364. print(info, "%s", agxbuse(&buf));
  365. agxbprint(&buf, " %.02f", r->h);
  366. agxbuf_trim_zeros(&buf);
  367. print(info, "%s", agxbuse(&buf));
  368. agxbfree(&buf);
  369. }
  370. static void printPolyline(xdot_polyline * p, pf print, void *info)
  371. {
  372. agxbuf buf = {0};
  373. print(info, " %" PRISIZE_T, p->cnt);
  374. for (size_t i = 0; i < p->cnt; i++) {
  375. agxbprint(&buf, " %.02f", p->pts[i].x);
  376. agxbuf_trim_zeros(&buf);
  377. print(info, "%s", agxbuse(&buf));
  378. agxbprint(&buf, " %.02f", p->pts[i].y);
  379. agxbuf_trim_zeros(&buf);
  380. print(info, "%s", agxbuse(&buf));
  381. }
  382. agxbfree(&buf);
  383. }
  384. static void printString(char *p, pf print, void *info)
  385. {
  386. print(info, " %" PRISIZE_T " -%s", strlen(p), p);
  387. }
  388. static void printFloat(double f, pf print, void *info, int space) {
  389. agxbuf buf = {0};
  390. if (space)
  391. agxbprint(&buf, " %.02f", f);
  392. else
  393. agxbprint(&buf, "%.02f", f);
  394. agxbuf_trim_zeros(&buf);
  395. print(info, "%s", agxbuse(&buf));
  396. agxbfree(&buf);
  397. }
  398. static void printAlign(xdot_align a, pf print, void *info)
  399. {
  400. switch (a) {
  401. case xd_left:
  402. print(info, " -1");
  403. break;
  404. case xd_right:
  405. print(info, " 1");
  406. break;
  407. case xd_center:
  408. print(info, " 0");
  409. break;
  410. default:
  411. UNREACHABLE();
  412. }
  413. }
  414. static void
  415. toGradString (agxbuf* xb, xdot_color* cp)
  416. {
  417. int i, n_stops;
  418. xdot_color_stop* stops;
  419. if (cp->type == xd_linear) {
  420. agxbputc (xb, '[');
  421. printFloat (cp->u.ling.x0, (pf)agxbprint, xb, 0);
  422. printFloat (cp->u.ling.y0, (pf)agxbprint, xb, 1);
  423. printFloat (cp->u.ling.x1, (pf)agxbprint, xb, 1);
  424. printFloat (cp->u.ling.y1, (pf)agxbprint, xb, 1);
  425. n_stops = cp->u.ling.n_stops;
  426. stops = cp->u.ling.stops;
  427. }
  428. else {
  429. agxbputc (xb, '(');
  430. printFloat (cp->u.ring.x0, (pf)agxbprint, xb, 0);
  431. printFloat (cp->u.ring.y0, (pf)agxbprint, xb, 1);
  432. printFloat (cp->u.ring.r0, (pf)agxbprint, xb, 1);
  433. printFloat (cp->u.ring.x1, (pf)agxbprint, xb, 1);
  434. printFloat (cp->u.ring.y1, (pf)agxbprint, xb, 1);
  435. printFloat (cp->u.ring.r1, (pf)agxbprint, xb, 1);
  436. n_stops = cp->u.ring.n_stops;
  437. stops = cp->u.ring.stops;
  438. }
  439. agxbprint(xb, " %d", n_stops);
  440. for (i = 0; i < n_stops; i++) {
  441. printFloat (stops[i].frac, (pf)agxbprint, xb, 1);
  442. printString (stops[i].color, (pf)agxbprint, xb);
  443. }
  444. if (cp->type == xd_linear)
  445. agxbputc (xb, ']');
  446. else
  447. agxbputc (xb, ')');
  448. }
  449. typedef void (*print_op)(xdot_op * op, pf print, void *info, int more);
  450. static void printXDot_Op(xdot_op * op, pf print, void *info, int more)
  451. {
  452. agxbuf xb = {0};
  453. switch (op->kind) {
  454. case xd_filled_ellipse:
  455. print(info, "E");
  456. printRect(&op->u.ellipse, print, info);
  457. break;
  458. case xd_unfilled_ellipse:
  459. print(info, "e");
  460. printRect(&op->u.ellipse, print, info);
  461. break;
  462. case xd_filled_polygon:
  463. print(info, "P");
  464. printPolyline(&op->u.polygon, print, info);
  465. break;
  466. case xd_unfilled_polygon:
  467. print(info, "p");
  468. printPolyline(&op->u.polygon, print, info);
  469. break;
  470. case xd_filled_bezier:
  471. print(info, "b");
  472. printPolyline(&op->u.bezier, print, info);
  473. break;
  474. case xd_unfilled_bezier:
  475. print(info, "B");
  476. printPolyline(&op->u.bezier, print, info);
  477. break;
  478. case xd_pen_color:
  479. print(info, "c");
  480. printString(op->u.color, print, info);
  481. break;
  482. case xd_grad_pen_color:
  483. print(info, "c");
  484. toGradString (&xb, &op->u.grad_color);
  485. printString(agxbuse(&xb), print, info);
  486. break;
  487. case xd_fill_color:
  488. print(info, "C");
  489. printString(op->u.color, print, info);
  490. break;
  491. case xd_grad_fill_color:
  492. print(info, "C");
  493. toGradString (&xb, &op->u.grad_color);
  494. printString(agxbuse(&xb), print, info);
  495. break;
  496. case xd_polyline:
  497. print(info, "L");
  498. printPolyline(&op->u.polyline, print, info);
  499. break;
  500. case xd_text:
  501. print(info, "T %.f %.f", op->u.text.x, op->u.text.y);
  502. printAlign(op->u.text.align, print, info);
  503. print(info, " %.f", op->u.text.width);
  504. printString(op->u.text.text, print, info);
  505. break;
  506. case xd_font:
  507. print(info, "F");
  508. printFloat(op->u.font.size, print, info, 1);
  509. printString(op->u.font.name, print, info);
  510. break;
  511. case xd_fontchar:
  512. print(info, "t %u", op->u.fontchar);
  513. break;
  514. case xd_style:
  515. print(info, "S");
  516. printString(op->u.style, print, info);
  517. break;
  518. case xd_image:
  519. print(info, "I");
  520. printRect(&op->u.image.pos, print, info);
  521. printString(op->u.image.name, print, info);
  522. break;
  523. default: // invalid type; ignore
  524. break;
  525. }
  526. if (more)
  527. print(info, " ");
  528. agxbfree (&xb);
  529. }
  530. static void jsonRect(xdot_rect * r, pf print, void *info)
  531. {
  532. print(info, "[%.06f,%.06f,%.06f,%.06f]", r->x, r->y, r->w, r->h);
  533. }
  534. static void jsonPolyline(xdot_polyline * p, pf print, void *info)
  535. {
  536. print(info, "[");
  537. for (size_t i = 0; i < p->cnt; i++) {
  538. print(info, "%.06f,%.06f", p->pts[i].x, p->pts[i].y);
  539. if (i < p->cnt-1) print(info, ",");
  540. }
  541. print(info, "]");
  542. }
  543. static void jsonString(char *p, pf print, void *info)
  544. {
  545. char c;
  546. print(info, "\"");
  547. while ((c = *p++)) {
  548. if (c == '"') print(info, "\\\"");
  549. else if (c == '\\') print(info, "\\\\");
  550. else print(info, "%c", c);
  551. }
  552. print(info, "\"");
  553. }
  554. static void jsonXDot_Op(xdot_op * op, pf print, void *info, int more)
  555. {
  556. agxbuf xb = {0};
  557. switch (op->kind) {
  558. case xd_filled_ellipse:
  559. print(info, "{\"E\" : ");
  560. jsonRect(&op->u.ellipse, print, info);
  561. break;
  562. case xd_unfilled_ellipse:
  563. print(info, "{\"e\" : ");
  564. jsonRect(&op->u.ellipse, print, info);
  565. break;
  566. case xd_filled_polygon:
  567. print(info, "{\"P\" : ");
  568. jsonPolyline(&op->u.polygon, print, info);
  569. break;
  570. case xd_unfilled_polygon:
  571. print(info, "{\"p\" : ");
  572. jsonPolyline(&op->u.polygon, print, info);
  573. break;
  574. case xd_filled_bezier:
  575. print(info, "{\"b\" : ");
  576. jsonPolyline(&op->u.bezier, print, info);
  577. break;
  578. case xd_unfilled_bezier:
  579. print(info, "{\"B\" : ");
  580. jsonPolyline(&op->u.bezier, print, info);
  581. break;
  582. case xd_pen_color:
  583. print(info, "{\"c\" : ");
  584. jsonString(op->u.color, print, info);
  585. break;
  586. case xd_grad_pen_color:
  587. print(info, "{\"c\" : ");
  588. toGradString (&xb, &op->u.grad_color);
  589. jsonString(agxbuse(&xb), print, info);
  590. break;
  591. case xd_fill_color:
  592. print(info, "{\"C\" : ");
  593. jsonString(op->u.color, print, info);
  594. break;
  595. case xd_grad_fill_color:
  596. print(info, "{\"C\" : ");
  597. toGradString (&xb, &op->u.grad_color);
  598. jsonString(agxbuse(&xb), print, info);
  599. break;
  600. case xd_polyline:
  601. print(info, "{\"L\" :");
  602. jsonPolyline(&op->u.polyline, print, info);
  603. break;
  604. case xd_text:
  605. print(info, "{\"T\" : [ %.f, %.f,", op->u.text.x, op->u.text.y);
  606. printAlign(op->u.text.align, print, info);
  607. print(info, ", %.f,", op->u.text.width);
  608. jsonString(op->u.text.text, print, info);
  609. print(info, "]");
  610. break;
  611. case xd_font:
  612. print(info, "{\"F\" : [");
  613. op->kind = xd_font;
  614. printFloat(op->u.font.size, print, info, 1);
  615. print(info, ",");
  616. jsonString(op->u.font.name, print, info);
  617. print(info, "]");
  618. break;
  619. case xd_fontchar:
  620. print(info, "{\"t\" : %u", op->u.fontchar);
  621. break;
  622. case xd_style:
  623. print(info, "{\"S\" : ");
  624. jsonString(op->u.style, print, info);
  625. break;
  626. case xd_image:
  627. print(info, "{\"I\" : [");
  628. jsonRect(&op->u.image.pos, print, info);
  629. print(info, ",");
  630. jsonString(op->u.image.name, print, info);
  631. print(info, "]");
  632. break;
  633. default: // invalid type; ignore
  634. break;
  635. }
  636. if (more)
  637. print(info, "},\n");
  638. else
  639. print(info, "}\n");
  640. agxbfree (&xb);
  641. }
  642. static void _printXDot(xdot * x, pf print, void *info, print_op ofn)
  643. {
  644. xdot_op *op;
  645. char *base = (char *) (x->ops);
  646. for (size_t i = 0; i < x->cnt; i++) {
  647. op = (xdot_op *) (base + i * x->sz);
  648. ofn(op, print, info, i < x->cnt - 1);
  649. }
  650. }
  651. char *sprintXDot(xdot * x)
  652. {
  653. agxbuf xb = {0};
  654. _printXDot(x, (pf)agxbprint, &xb, printXDot_Op);
  655. return agxbdisown(&xb);
  656. }
  657. void fprintXDot(FILE * fp, xdot * x)
  658. {
  659. _printXDot(x, (pf)fprintf, fp, printXDot_Op);
  660. }
  661. void jsonXDot(FILE * fp, xdot * x)
  662. {
  663. fputs ("[\n", fp);
  664. _printXDot(x, (pf)fprintf, fp, jsonXDot_Op);
  665. fputs ("]\n", fp);
  666. }
  667. static void freeXOpData(xdot_op * x)
  668. {
  669. switch (x->kind) {
  670. case xd_filled_polygon:
  671. case xd_unfilled_polygon:
  672. free(x->u.polyline.pts);
  673. break;
  674. case xd_filled_bezier:
  675. case xd_unfilled_bezier:
  676. free(x->u.polyline.pts);
  677. break;
  678. case xd_polyline:
  679. free(x->u.polyline.pts);
  680. break;
  681. case xd_text:
  682. free(x->u.text.text);
  683. break;
  684. case xd_fill_color:
  685. case xd_pen_color:
  686. free(x->u.color);
  687. break;
  688. case xd_grad_fill_color:
  689. case xd_grad_pen_color:
  690. freeXDotColor (&x->u.grad_color);
  691. break;
  692. case xd_font:
  693. free(x->u.font.name);
  694. break;
  695. case xd_style:
  696. free(x->u.style);
  697. break;
  698. case xd_image:
  699. free(x->u.image.name);
  700. break;
  701. default:
  702. break;
  703. }
  704. }
  705. void freeXDot (xdot * x)
  706. {
  707. xdot_op *op;
  708. char *base;
  709. freefunc_t ff = x->freefunc;
  710. if (!x) return;
  711. base = (char *) (x->ops);
  712. for (size_t i = 0; i < x->cnt; i++) {
  713. op = (xdot_op *) (base + i * x->sz);
  714. if (ff) ff (op);
  715. freeXOpData(op);
  716. }
  717. free(base);
  718. free(x);
  719. }
  720. int statXDot (xdot* x, xdot_stats* sp)
  721. {
  722. xdot_op *op;
  723. char *base;
  724. if (!x || !sp) return 1;
  725. memset(sp, 0, sizeof(xdot_stats));
  726. sp->cnt = x->cnt;
  727. base = (char *) (x->ops);
  728. for (size_t i = 0; i < x->cnt; i++) {
  729. op = (xdot_op *) (base + i * x->sz);
  730. switch (op->kind) {
  731. case xd_filled_ellipse:
  732. case xd_unfilled_ellipse:
  733. sp->n_ellipse++;
  734. break;
  735. case xd_filled_polygon:
  736. case xd_unfilled_polygon:
  737. sp->n_polygon++;
  738. sp->n_polygon_pts += op->u.polygon.cnt;
  739. break;
  740. case xd_filled_bezier:
  741. case xd_unfilled_bezier:
  742. sp->n_bezier++;
  743. sp->n_bezier_pts += op->u.bezier.cnt;
  744. break;
  745. case xd_polyline:
  746. sp->n_polyline++;
  747. sp->n_polyline_pts += op->u.polyline.cnt;
  748. break;
  749. case xd_text:
  750. sp->n_text++;
  751. break;
  752. case xd_image:
  753. sp->n_image++;
  754. break;
  755. case xd_fill_color:
  756. case xd_pen_color:
  757. sp->n_color++;
  758. break;
  759. case xd_grad_fill_color:
  760. case xd_grad_pen_color:
  761. sp->n_gradcolor++;
  762. break;
  763. case xd_font:
  764. sp->n_font++;
  765. break;
  766. case xd_fontchar:
  767. sp->n_fontchar++;
  768. break;
  769. case xd_style:
  770. sp->n_style++;
  771. break;
  772. default :
  773. break;
  774. }
  775. }
  776. return 0;
  777. }
  778. #define CHK1(s) if(!s){free(stops);return NULL;}
  779. /* radGradient:
  780. * Parse radial gradient spec
  781. * Return NULL on failure.
  782. */
  783. static char*
  784. radGradient (char* cp, xdot_color* clr)
  785. {
  786. char* s = cp;
  787. int i;
  788. double d;
  789. xdot_color_stop* stops = NULL;
  790. clr->type = xd_radial;
  791. s = parseReal(s, &clr->u.ring.x0);
  792. CHK1(s);
  793. s = parseReal(s, &clr->u.ring.y0);
  794. CHK1(s);
  795. s = parseReal(s, &clr->u.ring.r0);
  796. CHK1(s);
  797. s = parseReal(s, &clr->u.ring.x1);
  798. CHK1(s);
  799. s = parseReal(s, &clr->u.ring.y1);
  800. CHK1(s);
  801. s = parseReal(s, &clr->u.ring.r1);
  802. CHK1(s);
  803. s = parseInt(s, &clr->u.ring.n_stops);
  804. CHK1(s);
  805. stops = gv_calloc(clr->u.ring.n_stops, sizeof(stops[0]));
  806. for (i = 0; i < clr->u.ring.n_stops; i++) {
  807. s = parseReal(s, &d);
  808. CHK1(s);
  809. stops[i].frac = d;
  810. s = parseString(s, &stops[i].color);
  811. CHK1(s);
  812. }
  813. clr->u.ring.stops = stops;
  814. return cp;
  815. }
  816. /* linGradient:
  817. * Parse linear gradient spec
  818. * Return NULL on failure.
  819. */
  820. static char*
  821. linGradient (char* cp, xdot_color* clr)
  822. {
  823. char* s = cp;
  824. int i;
  825. double d;
  826. xdot_color_stop* stops = NULL;
  827. clr->type = xd_linear;
  828. s = parseReal(s, &clr->u.ling.x0);
  829. CHK1(s);
  830. s = parseReal(s, &clr->u.ling.y0);
  831. CHK1(s);
  832. s = parseReal(s, &clr->u.ling.x1);
  833. CHK1(s);
  834. s = parseReal(s, &clr->u.ling.y1);
  835. CHK1(s);
  836. s = parseInt(s, &clr->u.ling.n_stops);
  837. CHK1(s);
  838. stops = gv_calloc(clr->u.ling.n_stops, sizeof(stops[0]));
  839. for (i = 0; i < clr->u.ling.n_stops; i++) {
  840. s = parseReal(s, &d);
  841. CHK1(s);
  842. stops[i].frac = d;
  843. s = parseString(s, &stops[i].color);
  844. CHK1(s);
  845. }
  846. clr->u.ling.stops = stops;
  847. return cp;
  848. }
  849. /* parseXDotColor:
  850. * Parse xdot color spec: ordinary or gradient
  851. * The result is stored in clr.
  852. * Return NULL on failure.
  853. */
  854. char*
  855. parseXDotColor (char* cp, xdot_color* clr)
  856. {
  857. char c = *cp;
  858. switch (c) {
  859. case '[' :
  860. return linGradient (cp+1, clr);
  861. break;
  862. case '(' :
  863. return radGradient (cp+1, clr);
  864. break;
  865. case '#' :
  866. case '/' :
  867. clr->type = xd_none;
  868. clr->u.clr = cp;
  869. return cp;
  870. break;
  871. default :
  872. if (gv_isalnum(c)) {
  873. clr->type = xd_none;
  874. clr->u.clr = cp;
  875. return cp;
  876. }
  877. else
  878. return NULL;
  879. }
  880. }
  881. void freeXDotColor (xdot_color* cp)
  882. {
  883. int i;
  884. if (cp->type == xd_linear) {
  885. for (i = 0; i < cp->u.ling.n_stops; i++) {
  886. free (cp->u.ling.stops[i].color);
  887. }
  888. free (cp->u.ling.stops);
  889. }
  890. else if (cp->type == xd_radial) {
  891. for (i = 0; i < cp->u.ring.n_stops; i++) {
  892. free (cp->u.ring.stops[i].color);
  893. }
  894. free (cp->u.ring.stops);
  895. }
  896. }
  897. /**
  898. * @dir lib/xdot
  899. * @brief parsing and deparsing of xdot operations, API xdot.h
  900. *
  901. * [man 3 xdot](https://graphviz.org/pdf/xdot.3.pdf)
  902. *
  903. */