actions.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876
  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. /*
  11. * Code for main functions in gpr
  12. */
  13. #include <ast/ast.h>
  14. #include <ast/error.h>
  15. #include <cgraph/gv_ctype.h>
  16. #include <gvpr/actions.h>
  17. #include <gvpr/compile.h>
  18. #include <limits.h>
  19. #include <stdbool.h>
  20. #include <stddef.h>
  21. #include <stdint.h>
  22. #include <stdio.h>
  23. #include <string.h>
  24. #include <unistd.h>
  25. #include <util/agxbuf.h>
  26. #include <util/alloc.h>
  27. #include <util/strcasecmp.h>
  28. #include <util/unreachable.h>
  29. #include <util/unused.h>
  30. #define KINDS(p) \
  31. ((AGTYPE(p) == AGRAPH) ? "graph" : (AGTYPE(p) == AGNODE) ? "node" : "edge")
  32. static int iofread(void *chan, char *buf, int bufsize) {
  33. FILE *fp = chan;
  34. return (int)read(fileno(fp), buf, bufsize);
  35. }
  36. static int ioputstr(void *chan, const char *str) { return fputs(str, chan); }
  37. static int ioflush(void *chan) { return fflush(chan); }
  38. static Agiodisc_t gprIoDisc = {iofread, ioputstr, ioflush};
  39. /* sameG:
  40. * Return common root if objects belong to same root graph.
  41. * NULL otherwise
  42. */
  43. Agraph_t *sameG(void *p1, void *p2, char *fn, char *msg) {
  44. Agobj_t *obj1 = OBJ(p1);
  45. Agobj_t *obj2 = OBJ(p2);
  46. Agraph_t *root;
  47. root = agroot(agraphof(obj1));
  48. if (root != agroot(agraphof(obj2))) {
  49. if (msg)
  50. error(ERROR_WARNING, "%s in %s() belong to different graphs", msg, fn);
  51. else
  52. error(ERROR_WARNING, "%s and %s in %s() belong to different graphs",
  53. KINDS(obj1), KINDS(obj2), fn);
  54. return 0;
  55. } else
  56. return root;
  57. }
  58. /* indexOf:
  59. * Return index of leftmost string s2 in string s1, or -1
  60. */
  61. int indexOf(char *s1, char *s2) {
  62. char *s = strstr(s1, s2);
  63. return s == NULL ? -1 : (int)(s - s1);
  64. }
  65. /* rindexOf:
  66. * Return index of rightmost string s2 in string s1, or -1
  67. */
  68. long rindexOf(char *s1, char *s2) {
  69. char c1 = *s2;
  70. char *p;
  71. size_t len1 = strlen(s1);
  72. size_t len2 = strlen(s2);
  73. if (c1 == '\0') {
  74. assert(len1 <= LONG_MAX);
  75. return (long)len1;
  76. }
  77. if (len2 > len1)
  78. return -1;
  79. p = s1 + (len1 - len2);
  80. while (true) {
  81. if (strncmp(p, s2, len2) == 0)
  82. return p - s1;
  83. if (p == s1)
  84. break;
  85. p--;
  86. }
  87. return -1;
  88. }
  89. /* match:
  90. * Return index of pattern pat in string str, or SIZE_MAX
  91. */
  92. size_t match(char *str, char *pat) {
  93. size_t sub[2];
  94. if (strgrpmatch(str, pat, sub, 1, 0)) {
  95. return (sub[0]);
  96. } else
  97. return SIZE_MAX;
  98. }
  99. /* copyAttr:
  100. * Copy attributes from src to tgt. Overrides currently
  101. * defined values.
  102. * FIX: we should probably use the default value of the source
  103. * graph when initializing the attribute, rather than "".
  104. * NOTE: We do not assume src and tgt have the same kind.
  105. */
  106. int copyAttr(Agobj_t *src, Agobj_t *tgt) {
  107. Agraph_t *srcg;
  108. Agraph_t *tgtg;
  109. Agsym_t *sym = 0;
  110. Agsym_t *tsym = 0;
  111. int skind = AGTYPE(src);
  112. int tkind = AGTYPE(tgt);
  113. char *val;
  114. srcg = agraphof(src);
  115. tgtg = agraphof(tgt);
  116. while ((sym = agnxtattr(srcg, skind, sym))) {
  117. tsym = agattrsym(tgt, sym->name);
  118. if (!tsym)
  119. tsym = agattr(tgtg, tkind, sym->name, sym->defval);
  120. val = agxget(src, sym);
  121. if (aghtmlstr(val)) {
  122. val = agstrdup_html(tgtg, val);
  123. agxset(tgt, tsym, val);
  124. agstrfree(tgtg, val);
  125. } else
  126. agxset(tgt, tsym, val);
  127. }
  128. return 0;
  129. }
  130. /* copy:
  131. * Create new object of type AGTYPE(obj) with all of its
  132. * attributes.
  133. * If obj is an edge, only create end nodes if necessary.
  134. * If obj is a graph, if g is null, create a top-level
  135. * graph. Otherwise, create a subgraph of g.
  136. * Assume obj != NULL.
  137. */
  138. Agobj_t *copy(Agraph_t *g, Agobj_t *obj) {
  139. Agobj_t *nobj = 0;
  140. Agedge_t *e;
  141. Agnode_t *h;
  142. Agnode_t *t;
  143. int kind = AGTYPE(obj);
  144. char *name;
  145. if (kind != AGRAPH && !g) {
  146. exerror("NULL graph with non-graph object in copy()");
  147. return 0;
  148. }
  149. switch (kind) {
  150. case AGNODE:
  151. name = agnameof(obj);
  152. nobj = (Agobj_t *)openNode(g, name);
  153. break;
  154. case AGRAPH:
  155. name = agnameof(obj);
  156. if (g)
  157. nobj = (Agobj_t *)openSubg(g, name);
  158. else
  159. nobj = (Agobj_t *)openG(name, ((Agraph_t *)obj)->desc);
  160. break;
  161. case AGINEDGE:
  162. case AGOUTEDGE:
  163. e = (Agedge_t *)obj;
  164. t = openNode(g, agnameof(agtail(e)));
  165. h = openNode(g, agnameof(aghead(e)));
  166. name = agnameof(AGMKOUT(e));
  167. nobj = (Agobj_t *)openEdge(g, t, h, name);
  168. break;
  169. default:
  170. UNREACHABLE();
  171. }
  172. if (nobj)
  173. copyAttr(obj, nobj);
  174. return nobj;
  175. }
  176. typedef struct {
  177. Dtlink_t link;
  178. Agedge_t *key;
  179. Agedge_t *val;
  180. } edgepair_t;
  181. static Agedge_t *mapEdge(Dt_t *emap, Agedge_t *e) {
  182. edgepair_t *ep = dtmatch(emap, &e);
  183. if (ep)
  184. return ep->val;
  185. else
  186. return NULL;
  187. }
  188. /* cloneSubg:
  189. * Clone subgraph sg in tgt.
  190. */
  191. static Agraph_t *cloneSubg(Agraph_t *tgt, Agraph_t *g, Dt_t *emap) {
  192. Agraph_t *ng;
  193. Agraph_t *sg;
  194. Agnode_t *t;
  195. Agnode_t *newt;
  196. Agedge_t *e;
  197. Agedge_t *newe;
  198. char *name;
  199. ng = (Agraph_t *)copy(tgt, OBJ(g));
  200. if (!ng)
  201. return 0;
  202. for (t = agfstnode(g); t; t = agnxtnode(g, t)) {
  203. newt = agnode(tgt, agnameof(t), 0);
  204. if (!newt) {
  205. exerror("node %s not found in cloned graph %s", agnameof(t),
  206. agnameof(tgt));
  207. return 0;
  208. } else
  209. agsubnode(ng, newt, 1);
  210. }
  211. for (t = agfstnode(g); t; t = agnxtnode(g, t)) {
  212. for (e = agfstout(g, t); e; e = agnxtout(g, e)) {
  213. newe = mapEdge(emap, e);
  214. if (!newe) {
  215. name = agnameof(AGMKOUT(e));
  216. if (name)
  217. exerror("edge (%s,%s)[%s] not found in cloned graph %s",
  218. agnameof(agtail(e)), agnameof(aghead(e)), name,
  219. agnameof(tgt));
  220. else
  221. exerror("edge (%s,%s) not found in cloned graph %s",
  222. agnameof(agtail(e)), agnameof(aghead(e)), agnameof(tgt));
  223. return 0;
  224. } else
  225. agsubedge(ng, newe, 1);
  226. }
  227. }
  228. for (sg = agfstsubg(g); sg; sg = agnxtsubg(sg)) {
  229. if (!cloneSubg(ng, sg, emap)) {
  230. exerror("error cloning subgraph %s from graph %s", agnameof(sg),
  231. agnameof(g));
  232. return 0;
  233. }
  234. }
  235. return ng;
  236. }
  237. static int cmppair(void *k1, void *k2) {
  238. const Agedge_t **key1 = k1;
  239. const Agedge_t **key2 = k2;
  240. if (*key1 > *key2)
  241. return 1;
  242. else if (*key1 < *key2)
  243. return -1;
  244. else
  245. return 0;
  246. }
  247. static Dtdisc_t edgepair = {
  248. .key = offsetof(edgepair_t, key),
  249. .size = sizeof(Agedge_t *),
  250. .link = offsetof(edgepair_t, link),
  251. .comparf = cmppair,
  252. };
  253. /* cloneGraph:
  254. * Clone node, edge and subgraph structure from src to tgt.
  255. */
  256. static void cloneGraph(Agraph_t *tgt, Agraph_t *src) {
  257. Agedge_t *e;
  258. Agedge_t *ne;
  259. Agnode_t *t;
  260. Agraph_t *sg;
  261. char *name;
  262. Dt_t *emap = dtopen(&edgepair, Dtoset);
  263. edgepair_t *data = gv_calloc(agnedges(src), sizeof(edgepair_t));
  264. edgepair_t *ep = data;
  265. for (t = agfstnode(src); t; t = agnxtnode(src, t)) {
  266. if (!copy(tgt, OBJ(t))) {
  267. exerror("error cloning node %s from graph %s", agnameof(t),
  268. agnameof(src));
  269. }
  270. }
  271. for (t = agfstnode(src); t; t = agnxtnode(src, t)) {
  272. for (e = agfstout(src, t); e; e = agnxtout(src, e)) {
  273. if (!(ne = (Agedge_t *)copy(tgt, OBJ(e)))) {
  274. name = agnameof(AGMKOUT(e));
  275. if (name)
  276. exerror("error cloning edge (%s,%s)[%s] from graph %s",
  277. agnameof(agtail(e)), agnameof(aghead(e)), name,
  278. agnameof(src));
  279. else
  280. exerror("error cloning edge (%s,%s) from graph %s",
  281. agnameof(agtail(e)), agnameof(aghead(e)), agnameof(src));
  282. goto done;
  283. }
  284. ep->key = e;
  285. ep->val = ne;
  286. dtinsert(emap, ep++);
  287. }
  288. }
  289. for (sg = agfstsubg(src); sg; sg = agnxtsubg(sg)) {
  290. if (!cloneSubg(tgt, sg, emap)) {
  291. exerror("error cloning subgraph %s from graph %s", agnameof(sg),
  292. agnameof(src));
  293. }
  294. }
  295. done:
  296. dtclose(emap);
  297. free(data);
  298. }
  299. /* cloneG:
  300. */
  301. Agraph_t *cloneG(Agraph_t *g, char *name) {
  302. Agraph_t *ng;
  303. if (!name || *name == '\0')
  304. name = agnameof(g);
  305. ng = openG(name, g->desc);
  306. if (ng) {
  307. copyAttr((Agobj_t *)g, (Agobj_t *)ng);
  308. cloneGraph(ng, g);
  309. }
  310. return ng;
  311. }
  312. /* cloneO:
  313. * Create new object of type AGTYPE(obj) with all of its
  314. * attributes and substructure.
  315. * If obj is an edge, end nodes are cloned if necessary.
  316. * If obj is a graph, if g is null, create a clone top-level
  317. * graph. Otherwise, create a clone subgraph of g.
  318. * Assume obj != NULL.
  319. */
  320. Agobj_t *cloneO(Agraph_t *g, Agobj_t *obj) {
  321. Agobj_t *nobj = 0;
  322. Agedge_t *e;
  323. Agnode_t *h;
  324. Agnode_t *t;
  325. int kind = AGTYPE(obj);
  326. char *name;
  327. if (kind != AGRAPH && !g) {
  328. exerror("NULL graph with non-graph object in clone()");
  329. return 0;
  330. }
  331. switch (kind) {
  332. case AGNODE: /* same as copy node */
  333. name = agnameof(obj);
  334. nobj = (Agobj_t *)openNode(g, name);
  335. if (nobj)
  336. copyAttr(obj, nobj);
  337. break;
  338. case AGRAPH:
  339. name = agnameof(obj);
  340. if (g)
  341. nobj = (Agobj_t *)openSubg(g, name);
  342. else
  343. nobj = (Agobj_t *)openG(name, ((Agraph_t *)obj)->desc);
  344. if (nobj) {
  345. copyAttr(obj, nobj);
  346. cloneGraph((Agraph_t *)nobj, (Agraph_t *)obj);
  347. }
  348. break;
  349. case AGINEDGE:
  350. case AGOUTEDGE:
  351. e = (Agedge_t *)obj;
  352. t = (Agnode_t *)cloneO(g, OBJ(agtail(e)));
  353. h = (Agnode_t *)cloneO(g, OBJ(aghead(e)));
  354. name = agnameof(AGMKOUT(e));
  355. nobj = (Agobj_t *)openEdge(g, t, h, name);
  356. if (nobj)
  357. copyAttr(obj, nobj);
  358. break;
  359. default:
  360. UNREACHABLE();
  361. }
  362. return nobj;
  363. }
  364. #define CCMARKED(n) (((nData(n))->iu.integer) & 2)
  365. #define CCMARK(n) (((nData(n))->iu.integer) |= 2)
  366. #define CCUNMARK(n) (((nData(n))->iu.integer) &= ~2)
  367. static void cc_dfs(Agraph_t *g, Agraph_t *comp, Agnode_t *n) {
  368. Agedge_t *e;
  369. Agnode_t *other;
  370. CCMARK(n);
  371. agidnode(comp, AGID(n), 1);
  372. for (e = agfstedge(g, n); e; e = agnxtedge(g, e, n)) {
  373. if (agtail(e) == n)
  374. other = aghead(e);
  375. else
  376. other = agtail(e);
  377. if (!CCMARKED(other))
  378. cc_dfs(g, comp, other);
  379. }
  380. }
  381. /* compOf:
  382. * Return connected component of node.
  383. */
  384. Agraph_t *compOf(Agraph_t *g, Agnode_t *n) {
  385. Agraph_t *cg;
  386. Agnode_t *np;
  387. static int id;
  388. char name[64];
  389. if (!(n = agidnode(g, AGID(n), 0)))
  390. return 0; /* n not in g */
  391. for (np = agfstnode(g); np; np = agnxtnode(g, np))
  392. CCUNMARK(np);
  393. snprintf(name, sizeof(name), "_cc_%d", id++);
  394. cg = openSubg(g, name);
  395. cc_dfs(g, cg, n);
  396. return cg;
  397. }
  398. /* isEdge:
  399. * Return edge, if any, between t and h with given key.
  400. * Edge is in g.
  401. */
  402. Agedge_t *isEdge(Agraph_t *g, Agnode_t *t, Agnode_t *h, char *key) {
  403. Agraph_t *root;
  404. root = sameG(t, h, "isEdge", "tail and head node");
  405. if (!root)
  406. return 0;
  407. if (g) {
  408. if (root != agroot(g))
  409. return 0;
  410. } else
  411. g = root;
  412. return agedge(g, t, h, key, 0);
  413. }
  414. /* addNode:
  415. * Insert node n into subgraph g.
  416. * Return image of n
  417. */
  418. Agnode_t *addNode(Agraph_t *gp, Agnode_t *np, int doAdd) {
  419. if (!sameG(gp, np, "addNode", 0))
  420. return 0;
  421. return agsubnode(gp, np, doAdd);
  422. }
  423. /* addEdge:
  424. * Insert edge e into subgraph g.
  425. * Return image of e
  426. */
  427. Agedge_t *addEdge(Agraph_t *gp, Agedge_t *ep, int doAdd) {
  428. if (!sameG(gp, ep, "addEdge", 0))
  429. return 0;
  430. return agsubedge(gp, ep, doAdd);
  431. }
  432. /* lockGraph:
  433. * Set lock so that graph g will not be deleted.
  434. * g must be a root graph.
  435. * If v > 0, set lock
  436. * If v = 0, unset lock and delete graph is necessary.
  437. * If v < 0, no op
  438. * Always return previous lock state.
  439. * Return -1 on error.
  440. */
  441. int lockGraph(Agraph_t *g, int v) {
  442. gdata *data;
  443. if (g != agroot(g)) {
  444. error(ERROR_WARNING, "Graph argument to lock() is not a root graph");
  445. return -1;
  446. }
  447. data = gData(g);
  448. const int oldv = data->lock.locked;
  449. if (v > 0)
  450. data->lock.locked = true;
  451. else if (v == 0 && oldv) {
  452. if (data->lock.zombie)
  453. agclose(g);
  454. else
  455. data->lock = (lock_t){0};
  456. }
  457. return oldv;
  458. }
  459. /* deleteObj:
  460. * Remove obj from g.
  461. * obj may belong to a subgraph of g, so we first must map
  462. * obj to its version in g.
  463. * If g is null, remove object from root graph.
  464. * If obj is a (sub)graph, close it. The g parameter is unused.
  465. * Return 0 on success, non-zero on failure.
  466. */
  467. int deleteObj(Agraph_t *g, Agobj_t *obj) {
  468. gdata *data;
  469. if (AGTYPE(obj) == AGRAPH) {
  470. g = (Agraph_t *)obj;
  471. if (g != agroot(g))
  472. return agclose(g);
  473. data = gData(g);
  474. if (data->lock.locked) {
  475. error(ERROR_WARNING, "Cannot delete locked graph %s", agnameof(g));
  476. data->lock.zombie = true;
  477. return -1;
  478. } else
  479. return agclose(g);
  480. }
  481. /* node or edge */
  482. if (!g)
  483. g = agroot(agraphof(obj));
  484. if (obj)
  485. return agdelete(g, obj);
  486. else
  487. return -1;
  488. }
  489. /* sfioWrite:
  490. * If the graph is passed in from a library, its output discipline
  491. * might not use sfio. In this case, we push an sfio discipline on
  492. * the graph, write it, and then pop it off.
  493. */
  494. int sfioWrite(Agraph_t *g, FILE *fp) {
  495. int rv;
  496. Agiodisc_t *saveio = g->clos->disc.io;
  497. g->clos->disc.io = &gprIoDisc;
  498. rv = agwrite(g, fp);
  499. g->clos->disc.io = saveio;
  500. return rv;
  501. }
  502. /* writeFile:
  503. * Write graph into file f.
  504. * Return 0 on success
  505. */
  506. int writeFile(Agraph_t *g, char *f) {
  507. int rv;
  508. if (!f) {
  509. exerror("NULL string passed to writeG");
  510. return 1;
  511. }
  512. FILE *fp = fopen(f, "w");
  513. if (!fp) {
  514. exwarn("Could not open %s for writing in writeG", f);
  515. return 1;
  516. }
  517. rv = sfioWrite(g, fp);
  518. fclose(fp);
  519. return rv;
  520. }
  521. /* readFile:
  522. * Read graph from file f.
  523. * Return 0 on failure
  524. */
  525. Agraph_t *readFile(char *f) {
  526. Agraph_t *gp;
  527. if (!f) {
  528. exerror("NULL string passed to readG");
  529. return 0;
  530. }
  531. FILE *fp = fopen(f, "r");
  532. if (!fp) {
  533. exwarn("Could not open %s for reading in readG", f);
  534. return 0;
  535. }
  536. gp = readG(fp);
  537. fclose(fp);
  538. return gp;
  539. }
  540. int fwriteFile(Expr_t *ex, Agraph_t *g, long long fd) {
  541. FILE *sp;
  542. if (fd < 0 || fd >= (long long)elementsof(ex->file) || !(sp = ex->file[fd])) {
  543. exerror("fwriteG: %lld: invalid descriptor", fd);
  544. return 0;
  545. }
  546. return sfioWrite(g, sp);
  547. }
  548. Agraph_t *freadFile(Expr_t *ex, long long fd) {
  549. FILE *sp;
  550. if (fd < 0 || fd >= (long long)elementsof(ex->file) || !(sp = ex->file[fd])) {
  551. exerror("freadG: %lld: invalid descriptor", fd);
  552. return 0;
  553. }
  554. return readG(sp);
  555. }
  556. int openFile(Expr_t *ex, const char *fname, const char *mode) {
  557. int idx;
  558. /* find open index */
  559. for (idx = 3; idx < elementsof(ex->file); idx++)
  560. if (!ex->file[idx])
  561. break;
  562. if (idx == elementsof(ex->file)) {
  563. exerror("openF: no available descriptors");
  564. return -1;
  565. }
  566. ex->file[idx] = fopen(fname, mode);
  567. if (ex->file[idx])
  568. return idx;
  569. else
  570. return -1;
  571. }
  572. int closeFile(Expr_t *ex, long long fd) {
  573. int rv;
  574. if (0 <= fd && fd <= 2) {
  575. exerror("closeF: cannot close standard stream %lld", fd);
  576. return -1;
  577. }
  578. if (fd < 0 || fd >= (long long)elementsof(ex->file)) {
  579. exerror("closeG: %lld: invalid descriptor", fd);
  580. return -1;
  581. }
  582. if (!ex->file[fd]) {
  583. exerror("closeF: stream %lld not open", fd);
  584. return -1;
  585. }
  586. rv = fclose(ex->file[fd]);
  587. if (!rv)
  588. ex->file[fd] = 0;
  589. return rv;
  590. }
  591. /*
  592. * Read single line from stream.
  593. * Return "" on EOF.
  594. */
  595. char *readLine(Expr_t *ex, long long fd) {
  596. FILE *sp;
  597. int c;
  598. char *line;
  599. if (fd < 0 || fd >= (long long)elementsof(ex->file) || !(sp = ex->file[fd])) {
  600. exerror("readL: %lld: invalid descriptor", fd);
  601. return "";
  602. }
  603. agxbuf tmps = {0};
  604. while ((c = getc(sp)) > 0 && c != '\n')
  605. agxbputc(&tmps, (char)c);
  606. if (c == '\n')
  607. agxbputc(&tmps, (char)c);
  608. line = exstring(ex, agxbuse(&tmps));
  609. agxbfree(&tmps);
  610. return line;
  611. }
  612. /* compare:
  613. * Lexicographic ordering of objects.
  614. */
  615. int compare(Agobj_t *l, Agobj_t *r) {
  616. char lkind, rkind;
  617. if (l == NULL) {
  618. if (r == NULL)
  619. return 0;
  620. else
  621. return -1;
  622. } else if (r == NULL) {
  623. return 1;
  624. }
  625. if (AGID(l) < AGID(r))
  626. return -1;
  627. else if (AGID(l) > AGID(r))
  628. return 1;
  629. lkind = AGTYPE(l);
  630. rkind = AGTYPE(r);
  631. if (lkind == 3)
  632. lkind = 2;
  633. if (rkind == 3)
  634. rkind = 2;
  635. if (lkind == rkind)
  636. return 0;
  637. else if (lkind < rkind)
  638. return -1;
  639. else
  640. return 1;
  641. }
  642. /* toLower:
  643. * Convert characters to lowercase
  644. */
  645. char *toLower(Expr_t *pgm, char *src) {
  646. const size_t len = strlen(src);
  647. char *dst = exstralloc(pgm, len + 1);
  648. if (dst == NULL) {
  649. return NULL;
  650. }
  651. for (size_t i = 0; i < len; ++i) {
  652. dst[i] = gv_tolower(src[i]);
  653. }
  654. dst[len] = '\0';
  655. return dst;
  656. }
  657. /* toUpper:
  658. * Convert characters to uppercase
  659. */
  660. char *toUpper(Expr_t *pgm, char *src) {
  661. const size_t len = strlen(src);
  662. char *dst = exstralloc(pgm, len + 1);
  663. if (dst == NULL) {
  664. return NULL;
  665. }
  666. for (size_t i = 0; i < len; ++i) {
  667. dst[i] = gv_toupper(src[i]);
  668. }
  669. dst[len] = '\0';
  670. return dst;
  671. }
  672. /* toHtml:
  673. * Create a string marked as HTML
  674. */
  675. char *toHtml(Agraph_t *g, char *arg) { return agstrdup_html(g, arg); }
  676. /* canon:
  677. * Canonicalize a string for printing.
  678. */
  679. char *canon(Expr_t *pgm, char *arg) {
  680. char *p;
  681. p = agcanonStr(arg);
  682. if (p != arg)
  683. p = exstring(pgm, p);
  684. return p;
  685. }
  686. #undef S
  687. // force the upcoming ../common/colxlate.c functions to not be exported
  688. #ifdef COLORPROCS_API
  689. #undef COLORPROCS_API
  690. #endif
  691. #ifdef GVDLL
  692. #undef GVDLL
  693. #endif
  694. #ifdef GVC_EXPORTS
  695. #undef GVC_EXPORTS
  696. #endif
  697. #define COLORPROCS_API static UNUSED
  698. #include "../common/colxlate.c"
  699. /* colorx:
  700. * RGB, RGBA, HSV, HSVA
  701. */
  702. char *colorx(Expr_t *ex, const char *incolor, char *fmt) {
  703. gvcolor_t color = {{{0}}, 0};
  704. color_type_t type;
  705. int rc;
  706. int alpha;
  707. if (*fmt == '\0' || *incolor == '\0')
  708. return "";
  709. if (*fmt == 'R') {
  710. type = RGBA_BYTE;
  711. if (!strcmp(fmt, "RGBA"))
  712. alpha = 1;
  713. else
  714. alpha = 0;
  715. } else if (*fmt == 'H') {
  716. type = HSVA_DOUBLE;
  717. if (!strcmp(fmt, "HSVA"))
  718. alpha = 1;
  719. else
  720. alpha = 0;
  721. } else
  722. return "";
  723. rc = colorxlate(incolor, &color, type);
  724. if (rc != COLOR_OK)
  725. return "";
  726. agxbuf fp = {0};
  727. switch (type) {
  728. case HSVA_DOUBLE:
  729. agxbprint(&fp, "%.03f %.03f %.03f", color.u.HSVA[0], color.u.HSVA[1],
  730. color.u.HSVA[2]);
  731. if (alpha)
  732. agxbprint(&fp, " %.03f", color.u.HSVA[3]);
  733. break;
  734. case RGBA_BYTE:
  735. agxbprint(&fp, "#%02x%02x%02x", color.u.rgba[0], color.u.rgba[1],
  736. color.u.rgba[2]);
  737. if (alpha)
  738. agxbprint(&fp, "%02x", color.u.rgba[3]);
  739. break;
  740. default:
  741. break;
  742. }
  743. char *result = exstring(ex, agxbuse(&fp));
  744. agxbfree(&fp);
  745. return result;
  746. }
  747. #ifndef _WIN32
  748. #include <sys/param.h>
  749. #include <sys/times.h>
  750. #include <sys/types.h>
  751. #include <unistd.h>
  752. #ifndef HZ
  753. #define HZ 60
  754. #endif
  755. typedef struct tms mytime_t;
  756. #define GET_TIME(S) times(&(S))
  757. #define DIFF_IN_SECS(S, T) \
  758. ((S.tms_utime + S.tms_stime - T.tms_utime - T.tms_stime) / (double)HZ)
  759. #else
  760. #include <time.h>
  761. typedef clock_t mytime_t;
  762. #define GET_TIME(S) S = clock()
  763. #define DIFF_IN_SECS(S, T) ((S - T) / (double)CLOCKS_PER_SEC)
  764. #endif
  765. static mytime_t T;
  766. void gvstart_timer(void) { GET_TIME(T); }
  767. double gvelapsed_sec(void) {
  768. mytime_t S;
  769. double rv;
  770. GET_TIME(S);
  771. rv = DIFF_IN_SECS(S, T);
  772. return rv;
  773. }