input.c 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894
  1. /// @file
  2. /// @ingroup common_render
  3. /*************************************************************************
  4. * Copyright (c) 2011 AT&T Intellectual Property
  5. * All rights reserved. This program and the accompanying materials
  6. * are made available under the terms of the Eclipse Public License v1.0
  7. * which accompanies this distribution, and is available at
  8. * https://www.eclipse.org/legal/epl-v10.html
  9. *
  10. * Contributors: Details at https://graphviz.org
  11. *************************************************************************/
  12. #include <common/render.h>
  13. #include <common/htmltable.h>
  14. #include <errno.h>
  15. #include <gvc/gvc.h>
  16. #include <xdot/xdot.h>
  17. #include <cgraph/gv_ctype.h>
  18. #include <cgraph/gv_math.h>
  19. #include <limits.h>
  20. #include <stdbool.h>
  21. #include <stdlib.h>
  22. #include <string.h>
  23. #include <util/agxbuf.h>
  24. #include <util/alloc.h>
  25. #include <util/exit.h>
  26. #include <util/gv_fopen.h>
  27. #include <util/startswith.h>
  28. #include <util/strcasecmp.h>
  29. #include <util/streq.h>
  30. static char *usageFmt =
  31. "Usage: %s [-Vv?] [-(GNE)name=val] [-(KTlso)<val>] <dot files>\n";
  32. static char *genericItems = "\n\
  33. -V - Print version and exit\n\
  34. -v - Enable verbose mode \n\
  35. -Gname=val - Set graph attribute 'name' to 'val'\n\
  36. -Nname=val - Set node attribute 'name' to 'val'\n\
  37. -Ename=val - Set edge attribute 'name' to 'val'\n\
  38. -Tv - Set output format to 'v'\n\
  39. -Kv - Set layout engine to 'v' (overrides default based on command name)\n\
  40. -lv - Use external library 'v'\n\
  41. -ofile - Write output to 'file'\n\
  42. -O - Automatically generate an output filename based on the input filename with a .'format' appended. (Causes all -ofile options to be ignored.) \n\
  43. -P - Internally generate a graph of the current plugins. \n\
  44. -q[l] - Set level of message suppression (=1)\n\
  45. -s[v] - Scale input by 'v' (=72)\n\
  46. -y - Invert y coordinate in output\n";
  47. static char *neatoFlags =
  48. "(additional options for neato) [-x] [-n<v>]\n";
  49. static char *neatoItems = "\n\
  50. -n[v] - No layout mode 'v' (=1)\n\
  51. -x - Reduce graph\n";
  52. static char *fdpFlags =
  53. "(additional options for fdp) [-L(gO)] [-L(nUCT)<val>]\n";
  54. static char *fdpItems = "\n\
  55. -Lg - Don't use grid\n\
  56. -LO - Use old attractive force\n\
  57. -Ln<i> - Set number of iterations to i\n\
  58. -LU<i> - Set unscaled factor to i\n\
  59. -LC<v> - Set overlap expansion factor to v\n\
  60. -LT[*]<v> - Set temperature (temperature factor) to v\n";
  61. static char *configFlags = "(additional options for config) [-cv]\n";
  62. static char *configItems = "\n\
  63. -c - Configure plugins (Writes $prefix/lib/graphviz/config \n\
  64. with available plugin information. Needs write privilege.)\n\
  65. -? - Print usage and exit\n";
  66. /* Print usage information. If GvExitOnUsage is set, exit with
  67. * given exval, else return exval+1.
  68. *
  69. * @param argv0 Command name to use in usage message
  70. */
  71. int dotneato_usage(const char *argv0, int exval) {
  72. FILE *outs;
  73. if (exval > 0)
  74. outs = stderr;
  75. else
  76. outs = stdout;
  77. fprintf(outs, usageFmt, argv0);
  78. fputs(neatoFlags, outs);
  79. fputs(fdpFlags, outs);
  80. fputs(configFlags, outs);
  81. fputs(genericItems, outs);
  82. fputs(neatoItems, outs);
  83. fputs(fdpItems, outs);
  84. fputs(configItems, outs);
  85. if (GvExitOnUsage && exval >= 0)
  86. graphviz_exit(exval);
  87. return exval + 1;
  88. }
  89. /* Look for flag parameter. idx is index of current argument.
  90. * We assume argv[*idx] has the form "-x..." If there are characters
  91. * after the x, return
  92. * these, else if there are more arguments, return the next one,
  93. * else return NULL.
  94. */
  95. static char *getFlagOpt(int argc, char **argv, int *idx)
  96. {
  97. int i = *idx;
  98. char *arg = argv[i];
  99. if (arg[2])
  100. return arg + 2;
  101. if (i < argc - 1) {
  102. i++;
  103. arg = argv[i];
  104. if (*arg && *arg != '-') {
  105. *idx = i;
  106. return arg;
  107. }
  108. }
  109. return 0;
  110. }
  111. /* Partial implementation of real basename.
  112. * Skip over any trailing slashes or backslashes; then
  113. * find next (back)slash moving left; return string to the right.
  114. * If no next slash is found, return the whole string.
  115. */
  116. static char *dotneato_basename(char *pathname) {
  117. char* ret;
  118. char* s = pathname;
  119. if (*s == '\0') return pathname; /* empty string */
  120. #ifdef _WIN32
  121. /* On Windows, executables, by convention, end in ".exe". Thus,
  122. * this may be part of the path name and must be removed for
  123. * matching to work.
  124. */
  125. {
  126. char* dotp = strrchr (s, '.');
  127. if (dotp && !strcasecmp(dotp+1,"exe")) *dotp = '\0';
  128. }
  129. #endif
  130. while (*s) s++;
  131. s--;
  132. /* skip over trailing slashes, nulling out as we go */
  133. while (s > pathname && (*s == '/' || *s == '\\'))
  134. *s-- = '\0';
  135. if (s == pathname) ret = pathname;
  136. else {
  137. while (s > pathname && *s != '/' && *s != '\\') s--;
  138. if (*s == '/' || *s == '\\') ret = s+1;
  139. else ret = pathname;
  140. }
  141. #ifdef _WIN32
  142. /* On Windows, names are case-insensitive, so make name lower-case
  143. */
  144. gv_tolower_str(ret);
  145. #endif
  146. return ret;
  147. }
  148. static void use_library(GVC_t *gvc, const char *name)
  149. {
  150. static size_t cnt = 0;
  151. if (name) {
  152. const size_t old_nmemb = cnt == 0 ? cnt : cnt + 1;
  153. Lib = gv_recalloc(Lib, old_nmemb, cnt + 2, sizeof(const char *));
  154. Lib[cnt++] = name;
  155. Lib[cnt] = NULL;
  156. }
  157. gvc->common.lib = Lib;
  158. }
  159. static void global_def(char *dcl, int kind) {
  160. char *p;
  161. char *rhs = "true";
  162. agxbuf xb = {0};
  163. attrsym_t *sym;
  164. if ((p = strchr(dcl, '='))) {
  165. agxbput_n(&xb, dcl, (size_t)(p - dcl));
  166. rhs = p+1;
  167. }
  168. else
  169. agxbput(&xb, dcl);
  170. sym = agattr(NULL, kind, agxbuse(&xb), rhs);
  171. sym->fixed = 1;
  172. agxbfree(&xb);
  173. }
  174. static int gvg_init(GVC_t *gvc, graph_t *g, char *fn, int gidx)
  175. {
  176. GVG_t *gvg = gv_alloc(sizeof(GVG_t));
  177. if (!gvc->gvgs)
  178. gvc->gvgs = gvg;
  179. else
  180. gvc->gvg->next = gvg;
  181. gvc->gvg = gvg;
  182. gvg->gvc = gvc;
  183. gvg->g = g;
  184. gvg->input_filename = fn;
  185. gvg->graph_index = gidx;
  186. return 0;
  187. }
  188. static graph_t *P_graph;
  189. graph_t *gvPluginsGraph(GVC_t *gvc)
  190. {
  191. gvg_init(gvc, P_graph, "<internal>", 0);
  192. return P_graph;
  193. }
  194. /* Scan argv[] for allowed flags.
  195. * Return 0 on success; v+1 if calling function should call exit(v).
  196. * If -c is set, config file is created and we exit.
  197. */
  198. int dotneato_args_initialize(GVC_t * gvc, int argc, char **argv)
  199. {
  200. char c, *rest, *layout;
  201. const char *val;
  202. int i, v;
  203. int Kflag = 0;
  204. /* establish if we are running in a CGI environment */
  205. HTTPServerEnVar = getenv("SERVER_NAME");
  206. // test `$GV_FILE_PATH`, a legacy knob, is not set
  207. if (getenv("GV_FILE_PATH") != NULL) {
  208. fprintf(stderr, "$GV_FILE_PATH environment variable set; exiting\n"
  209. "\n"
  210. "This sandboxing mechanism is no longer supported\n");
  211. graphviz_exit(EXIT_FAILURE);
  212. }
  213. gvc->common.cmdname = dotneato_basename(argv[0]);
  214. if (gvc->common.verbose) {
  215. fprintf(stderr, "%s - %s version %s (%s)\n",
  216. gvc->common.cmdname, gvc->common.info[0],
  217. gvc->common.info[1], gvc->common.info[2]);
  218. }
  219. /* configure for available plugins */
  220. /* needs to know if "dot -c" is set (gvc->common.config) */
  221. /* must happen before trying to select any plugins */
  222. if (gvc->common.config) {
  223. gvconfig(gvc, gvc->common.config);
  224. graphviz_exit(0);
  225. }
  226. /* feed the globals */
  227. Verbose = gvc->common.verbose > UCHAR_MAX
  228. ? UCHAR_MAX
  229. : (unsigned char)gvc->common.verbose;
  230. size_t nfiles = 0;
  231. for (i = 1; i < argc; i++)
  232. if (argv[i] && argv[i][0] != '-')
  233. nfiles++;
  234. gvc->input_filenames = gv_calloc(nfiles + 1, sizeof(char *));
  235. nfiles = 0;
  236. for (i = 1; i < argc; i++) {
  237. if (argv[i] &&
  238. (startswith(argv[i], "-V") || strcmp(argv[i], "--version") == 0)) {
  239. fprintf(stderr, "%s - %s version %s (%s)\n",
  240. gvc->common.cmdname, gvc->common.info[0],
  241. gvc->common.info[1], gvc->common.info[2]);
  242. if (GvExitOnUsage) graphviz_exit(0);
  243. return 1;
  244. } else if (argv[i] &&
  245. (startswith(argv[i], "-?") || strcmp(argv[i], "--help") == 0)) {
  246. return dotneato_usage(argv[0], 0);
  247. } else if (argv[i] && startswith(argv[i], "--filepath=")) {
  248. free(Gvfilepath);
  249. Gvfilepath = gv_strdup(argv[i] + strlen("--filepath="));
  250. } else if (argv[i] && argv[i][0] == '-') {
  251. rest = &argv[i][2];
  252. switch (c = argv[i][1]) {
  253. case 'G':
  254. if (*rest)
  255. global_def(rest, AGRAPH);
  256. else {
  257. fprintf(stderr, "Missing argument for -G flag\n");
  258. return dotneato_usage(argv[0], 1);
  259. }
  260. break;
  261. case 'N':
  262. if (*rest)
  263. global_def(rest, AGNODE);
  264. else {
  265. fprintf(stderr, "Missing argument for -N flag\n");
  266. return dotneato_usage(argv[0], 1);
  267. }
  268. break;
  269. case 'E':
  270. if (*rest)
  271. global_def(rest, AGEDGE);
  272. else {
  273. fprintf(stderr, "Missing argument for -E flag\n");
  274. return dotneato_usage(argv[0], 1);
  275. }
  276. break;
  277. case 'T':
  278. val = getFlagOpt(argc, argv, &i);
  279. if (!val) {
  280. fprintf(stderr, "Missing argument for -T flag\n");
  281. return dotneato_usage(argv[0], 1);
  282. }
  283. if (!gvjobs_output_langname(gvc, val)) {
  284. const char *const fmts = gvplugin_list(gvc, API_device, val);
  285. fprintf(stderr, "Format: \"%s\" not recognized.", val);
  286. if (strlen(fmts) > 1) {
  287. fprintf(stderr, " Use one of:%s\n", fmts);
  288. } else {
  289. /* Q: Should 'dot -c' be suggested generally or only when val = "dot"? */
  290. fprintf(stderr, " No formats found.\nPerhaps \"dot -c\" needs to be run (with installer's privileges) to register the plugins?\n");
  291. }
  292. if (GvExitOnUsage) graphviz_exit(1);
  293. return 2;
  294. }
  295. break;
  296. case 'K':
  297. val = getFlagOpt(argc, argv, &i);
  298. if (!val) {
  299. fprintf(stderr, "Missing argument for -K flag\n");
  300. return dotneato_usage(argv[0], 1);
  301. }
  302. v = gvlayout_select(gvc, val);
  303. if (v == NO_SUPPORT) {
  304. fprintf(stderr, "There is no layout engine support for \"%s\"\n", val);
  305. if (streq(val, "dot")) {
  306. fprintf(stderr, "Perhaps \"dot -c\" needs to be run (with installer's privileges) to register the plugins?\n");
  307. }
  308. else {
  309. const char *const lyts = gvplugin_list(gvc, API_layout, val);
  310. if (strlen(lyts) > 1) {
  311. fprintf(stderr, " Use one of:%s\n", lyts);
  312. } else {
  313. /* Q: Should 'dot -c' be suggested generally or only when val = "dot"? */
  314. fprintf(stderr, " No layouts found.\nPerhaps \"dot -c\" needs to be run (with installer's privileges) to register the plugins?\n");
  315. }
  316. }
  317. if (GvExitOnUsage) graphviz_exit(1);
  318. return 2;
  319. }
  320. Kflag = 1;
  321. break;
  322. case 'P':
  323. P_graph = gvplugin_graph(gvc);
  324. break;
  325. case 'l':
  326. val = getFlagOpt(argc, argv, &i);
  327. if (!val) {
  328. fprintf(stderr, "Missing argument for -l flag\n");
  329. return dotneato_usage(argv[0], 1);
  330. }
  331. use_library(gvc, val);
  332. break;
  333. case 'o':
  334. val = getFlagOpt(argc, argv, &i);
  335. if (!val) {
  336. fprintf(stderr, "Missing argument for -o flag\n");
  337. return dotneato_usage(argv[0], 1);
  338. }
  339. if (! gvc->common.auto_outfile_names)
  340. gvjobs_output_filename(gvc, val);
  341. break;
  342. case 'q':
  343. if (*rest) {
  344. v = atoi(rest);
  345. if (v <= 0) {
  346. fprintf(stderr,
  347. "Invalid parameter \"%s\" for -q flag - ignored\n",
  348. rest);
  349. } else if (v == 1)
  350. agseterr(AGERR);
  351. else
  352. agseterr(AGMAX);
  353. } else
  354. agseterr(AGERR);
  355. break;
  356. case 's':
  357. if (*rest) {
  358. PSinputscale = atof(rest);
  359. if (PSinputscale < 0) {
  360. fprintf(stderr,
  361. "Invalid parameter \"%s\" for -s flag\n",
  362. rest);
  363. return dotneato_usage(argv[0], 1);
  364. }
  365. else if (is_exactly_zero(PSinputscale))
  366. PSinputscale = POINTS_PER_INCH;
  367. } else
  368. PSinputscale = POINTS_PER_INCH;
  369. break;
  370. case 'x':
  371. Reduce = true;
  372. break;
  373. case 'y':
  374. Y_invert = true;
  375. break;
  376. default:
  377. agerrorf("%s: option -%c unrecognized\n\n", gvc->common.cmdname,
  378. c);
  379. return dotneato_usage(argv[0], 1);
  380. }
  381. } else if (argv[i])
  382. gvc->input_filenames[nfiles++] = argv[i];
  383. }
  384. /* if no -K, use cmd name to set layout type */
  385. if (!Kflag) {
  386. layout = gvc->common.cmdname;
  387. if (streq(layout, "dot_static")
  388. || streq(layout, "dot_builtins")
  389. || streq(layout, "lt-dot")
  390. || streq(layout, "lt-dot_builtins")
  391. || streq(layout, "") /* when run as a process from Gvedit on Windows */
  392. )
  393. layout = "dot";
  394. i = gvlayout_select(gvc, layout);
  395. if (i == NO_SUPPORT) {
  396. fprintf(stderr, "There is no layout engine support for \"%s\"\n", layout);
  397. if (streq(layout, "dot")) {
  398. fprintf(stderr, "Perhaps \"dot -c\" needs to be run (with installer's privileges) to register the plugins?\n");
  399. } else {
  400. /* TODO: Detect empty results from gvplugin_list() and prompt to configure with '-c' */
  401. /* fprintf(stderr, "Use one of:%s\n", gvplugin_list(gvc, API_layout, "")); */
  402. char *lyts;
  403. lyts = gvplugin_list(gvc, API_layout, "");
  404. if (strlen(lyts) > 1) {
  405. fprintf(stderr, " Use one of:%s\n", lyts);
  406. } else {
  407. /* Q: Should 'dot -c' be suggested generally or only when val = "dot"? */
  408. fprintf(stderr, " No layouts found.\nPerhaps \"dot -c\" needs to be run (with installer's privileges) to register the plugins?\n");
  409. }
  410. }
  411. if (GvExitOnUsage) graphviz_exit(1);
  412. return 2;
  413. }
  414. }
  415. /* if no -Txxx, then set default format */
  416. if (!gvc->jobs || !gvc->jobs->output_langname) {
  417. if (!gvjobs_output_langname(gvc, "dot")) {
  418. fprintf(stderr,
  419. "Unable to find even the default \"-Tdot\" renderer. Has the config\nfile been generated by running \"dot -c\" with installer's privileges?\n");
  420. return 2;
  421. }
  422. }
  423. /* set persistent attributes here (if not already set from command line options) */
  424. if (!agattr(NULL, AGNODE, "label", 0))
  425. agattr(NULL, AGNODE, "label", NODENAME_ESC);
  426. return 0;
  427. }
  428. /* converts a graph attribute in inches to a pointf in points.
  429. * If only one number is given, it is used for both x and y.
  430. * Returns true if the attribute ends in '!'.
  431. */
  432. static bool getdoubles2ptf(graph_t *g, char *name, pointf *result) {
  433. char *p;
  434. int i;
  435. double xf, yf;
  436. char c = '\0';
  437. bool rv = false;
  438. if ((p = agget(g, name))) {
  439. i = sscanf(p, "%lf,%lf%c", &xf, &yf, &c);
  440. if (i > 1 && xf > 0 && yf > 0) {
  441. result->x = POINTS(xf);
  442. result->y = POINTS(yf);
  443. if (c == '!')
  444. rv = true;
  445. }
  446. else {
  447. c = '\0';
  448. i = sscanf(p, "%lf%c", &xf, &c);
  449. if (i > 0 && xf > 0) {
  450. result->y = result->x = POINTS(xf);
  451. if (c == '!') rv = true;
  452. }
  453. }
  454. }
  455. return rv;
  456. }
  457. void getdouble(graph_t * g, char *name, double *result)
  458. {
  459. char *p;
  460. double f;
  461. if ((p = agget(g, name))) {
  462. if (sscanf(p, "%lf", &f) >= 1)
  463. *result = f;
  464. }
  465. }
  466. graph_t *gvNextInputGraph(GVC_t *gvc)
  467. {
  468. graph_t *g = NULL;
  469. static char *fn;
  470. static FILE *fp;
  471. static FILE *oldfp;
  472. static int gidx;
  473. while (!g) {
  474. if (!fp) {
  475. if (!(fn = gvc->input_filenames[0])) {
  476. if (gvc->fidx++ == 0)
  477. fp = stdin;
  478. }
  479. else {
  480. while ((fn = gvc->input_filenames[gvc->fidx++]) && !(fp = gv_fopen(fn, "r"))) {
  481. agerrorf("%s: can't open %s: %s\n", gvc->common.cmdname, fn, strerror(errno));
  482. graphviz_errors++;
  483. }
  484. }
  485. }
  486. if (fp == NULL)
  487. break;
  488. if (oldfp != fp) {
  489. agsetfile(fn ? fn : "<stdin>");
  490. oldfp = fp;
  491. }
  492. g = agread(fp,NULL);
  493. if (g) {
  494. gvg_init(gvc, g, fn, gidx++);
  495. break;
  496. }
  497. if (fp != stdin)
  498. fclose (fp);
  499. oldfp = fp = NULL;
  500. gidx = 0;
  501. }
  502. return g;
  503. }
  504. /* Check if the charset attribute is defined for the graph and, if
  505. * so, return the corresponding internal value. If undefined, return
  506. * CHAR_UTF8
  507. */
  508. static unsigned char findCharset(graph_t *g) {
  509. char* p;
  510. p = late_nnstring(g,agfindgraphattr(g,"charset"),"utf-8");
  511. if (!strcasecmp(p,"latin-1")
  512. || !strcasecmp(p,"latin1")
  513. || !strcasecmp(p,"l1")
  514. || !strcasecmp(p,"ISO-8859-1")
  515. || !strcasecmp(p,"ISO_8859-1")
  516. || !strcasecmp(p,"ISO8859-1")
  517. || !strcasecmp(p,"ISO-IR-100"))
  518. return CHAR_LATIN1;
  519. if (!strcasecmp(p,"big-5")
  520. || !strcasecmp(p,"big5"))
  521. return CHAR_BIG5;
  522. if (!strcasecmp(p,"utf-8")
  523. || !strcasecmp(p,"utf8"))
  524. return CHAR_UTF8;
  525. agwarningf("Unsupported charset \"%s\" - assuming utf-8\n", p);
  526. return CHAR_UTF8;
  527. }
  528. /// Checks "ratio" attribute, if any, and sets enum type.
  529. static void setRatio(graph_t * g)
  530. {
  531. char *p;
  532. double ratio;
  533. if ((p = agget(g, "ratio"))) {
  534. if (streq(p, "auto")) {
  535. GD_drawing(g)->ratio_kind = R_AUTO;
  536. } else if (streq(p, "compress")) {
  537. GD_drawing(g)->ratio_kind = R_COMPRESS;
  538. } else if (streq(p, "expand")) {
  539. GD_drawing(g)->ratio_kind = R_EXPAND;
  540. } else if (streq(p, "fill")) {
  541. GD_drawing(g)->ratio_kind = R_FILL;
  542. } else {
  543. ratio = atof(p);
  544. if (ratio > 0.0) {
  545. GD_drawing(g)->ratio_kind = R_VALUE;
  546. GD_drawing(g)->ratio = ratio;
  547. }
  548. }
  549. }
  550. }
  551. void graph_init(graph_t * g, bool use_rankdir)
  552. {
  553. char *p;
  554. double xf;
  555. static char *rankname[] = { "local", "global", "none", NULL };
  556. static int rankcode[] = { LOCAL, GLOBAL, NOCLUST, LOCAL };
  557. static char *fontnamenames[] = {"gd","ps","svg", NULL};
  558. static int fontnamecodes[] = {NATIVEFONTS,PSFONTS,SVGFONTS,-1};
  559. int rankdir;
  560. GD_drawing(g) = gv_alloc(sizeof(layout_t));
  561. /* reparseable input */
  562. if ((p = agget(g, "postaction"))) { /* requires a graph wrapper for yyparse */
  563. agxbuf buf = {0};
  564. agxbprint(&buf, "%s { %s }", agisdirected(g) ? "digraph" : "graph", p);
  565. agmemconcat(g, agxbuse(&buf));
  566. agxbfree(&buf);
  567. }
  568. /* set this up fairly early in case any string sizes are needed */
  569. if ((p = agget(g, "fontpath")) || (p = getenv("DOTFONTPATH"))) {
  570. /* overide GDFONTPATH in local environment if dot
  571. * wants its own */
  572. #ifdef HAVE_SETENV
  573. setenv("GDFONTPATH", p, 1);
  574. #else
  575. static agxbuf buf;
  576. agxbprint(&buf, "GDFONTPATH=%s", p);
  577. putenv(agxbuse(&buf));
  578. #endif
  579. }
  580. GD_charset(g) = findCharset (g);
  581. if (!HTTPServerEnVar) {
  582. Gvimagepath = agget (g, "imagepath");
  583. if (!Gvimagepath) {
  584. Gvimagepath = Gvfilepath;
  585. }
  586. }
  587. GD_drawing(g)->quantum =
  588. late_double(g, agfindgraphattr(g, "quantum"), 0.0, 0.0);
  589. /* setting rankdir=LR is only defined in dot,
  590. * but having it set causes shape code and others to use it.
  591. * The result is confused output, so we turn it off unless requested.
  592. * This effective rankdir is stored in the bottom 2 bits of g->u.rankdir.
  593. * Sometimes, the code really needs the graph's rankdir, e.g., neato -n
  594. * with record shapes, so we store the real rankdir in the next 2 bits.
  595. */
  596. rankdir = RANKDIR_TB;
  597. if ((p = agget(g, "rankdir"))) {
  598. if (streq(p, "LR"))
  599. rankdir = RANKDIR_LR;
  600. else if (streq(p, "BT"))
  601. rankdir = RANKDIR_BT;
  602. else if (streq(p, "RL"))
  603. rankdir = RANKDIR_RL;
  604. }
  605. if (use_rankdir)
  606. SET_RANKDIR (g, (rankdir << 2) | rankdir);
  607. else
  608. SET_RANKDIR(g, rankdir << 2);
  609. xf = late_double(g, agfindgraphattr(g, "nodesep"),
  610. DEFAULT_NODESEP, MIN_NODESEP);
  611. GD_nodesep(g) = POINTS(xf);
  612. p = late_string(g, agfindgraphattr(g, "ranksep"), NULL);
  613. if (p) {
  614. if (sscanf(p, "%lf", &xf) == 0)
  615. xf = DEFAULT_RANKSEP;
  616. else {
  617. if (xf < MIN_RANKSEP)
  618. xf = MIN_RANKSEP;
  619. }
  620. if (strstr(p, "equally"))
  621. GD_exact_ranksep(g) = true;
  622. } else
  623. xf = DEFAULT_RANKSEP;
  624. GD_ranksep(g) = POINTS(xf);
  625. {
  626. int showboxes = late_int(g, agfindgraphattr(g, "showboxes"), 0, 0);
  627. if (showboxes > UCHAR_MAX) {
  628. showboxes = UCHAR_MAX;
  629. }
  630. GD_showboxes(g) = (unsigned char)showboxes;
  631. }
  632. p = late_string(g, agfindgraphattr(g, "fontnames"), NULL);
  633. GD_fontnames(g) = maptoken(p, fontnamenames, fontnamecodes);
  634. setRatio(g);
  635. GD_drawing(g)->filled = getdoubles2ptf(g, "size", &GD_drawing(g)->size);
  636. getdoubles2ptf(g, "page", &GD_drawing(g)->page);
  637. GD_drawing(g)->centered = mapbool(agget(g, "center"));
  638. if ((p = agget(g, "rotate")))
  639. GD_drawing(g)->landscape = atoi(p) == 90;
  640. else if ((p = agget(g, "orientation")))
  641. GD_drawing(g)->landscape = p[0] == 'l' || p[0] == 'L';
  642. else if ((p = agget(g, "landscape")))
  643. GD_drawing(g)->landscape = mapbool(p);
  644. p = agget(g, "clusterrank");
  645. CL_type = maptoken(p, rankname, rankcode);
  646. p = agget(g, "concentrate");
  647. Concentrate = mapbool(p);
  648. State = GVBEGIN;
  649. EdgeLabelsDone = 0;
  650. GD_drawing(g)->dpi = 0.0;
  651. if (((p = agget(g, "dpi")) && p[0])
  652. || ((p = agget(g, "resolution")) && p[0]))
  653. GD_drawing(g)->dpi = atof(p);
  654. do_graph_label(g);
  655. Initial_dist = MYHUGE;
  656. G_ordering = agfindgraphattr(g, "ordering");
  657. G_gradientangle = agfindgraphattr(g,"gradientangle");
  658. G_margin = agfindgraphattr(g, "margin");
  659. /* initialize nodes */
  660. N_height = agfindnodeattr(g, "height");
  661. N_width = agfindnodeattr(g, "width");
  662. N_shape = agfindnodeattr(g, "shape");
  663. N_color = agfindnodeattr(g, "color");
  664. N_fillcolor = agfindnodeattr(g, "fillcolor");
  665. N_style = agfindnodeattr(g, "style");
  666. N_fontsize = agfindnodeattr(g, "fontsize");
  667. N_fontname = agfindnodeattr(g, "fontname");
  668. N_fontcolor = agfindnodeattr(g, "fontcolor");
  669. N_label = agfindnodeattr(g, "label");
  670. if (!N_label)
  671. N_label = agattr(g, AGNODE, "label", NODENAME_ESC);
  672. N_xlabel = agfindnodeattr(g, "xlabel");
  673. N_showboxes = agfindnodeattr(g, "showboxes");
  674. N_penwidth = agfindnodeattr(g, "penwidth");
  675. N_ordering = agfindnodeattr(g, "ordering");
  676. /* attribs for polygon shapes */
  677. N_sides = agfindnodeattr(g, "sides");
  678. N_peripheries = agfindnodeattr(g, "peripheries");
  679. N_skew = agfindnodeattr(g, "skew");
  680. N_orientation = agfindnodeattr(g, "orientation");
  681. N_distortion = agfindnodeattr(g, "distortion");
  682. N_fixed = agfindnodeattr(g, "fixedsize");
  683. N_imagescale = agfindnodeattr(g, "imagescale");
  684. N_imagepos = agfindnodeattr(g, "imagepos");
  685. N_nojustify = agfindnodeattr(g, "nojustify");
  686. N_layer = agfindnodeattr(g, "layer");
  687. N_group = agfindnodeattr(g, "group");
  688. N_comment = agfindnodeattr(g, "comment");
  689. N_vertices = agfindnodeattr(g, "vertices");
  690. N_z = agfindnodeattr(g, "z");
  691. N_gradientangle = agfindnodeattr(g,"gradientangle");
  692. /* initialize edges */
  693. E_weight = agfindedgeattr(g, "weight");
  694. E_color = agfindedgeattr(g, "color");
  695. E_fillcolor = agfindedgeattr(g, "fillcolor");
  696. E_fontsize = agfindedgeattr(g, "fontsize");
  697. E_fontname = agfindedgeattr(g, "fontname");
  698. E_fontcolor = agfindedgeattr(g, "fontcolor");
  699. E_label = agfindedgeattr(g, "label");
  700. E_xlabel = agfindedgeattr(g, "xlabel");
  701. E_label_float = agfindedgeattr(g, "labelfloat");
  702. E_dir = agfindedgeattr(g, "dir");
  703. E_headlabel = agfindedgeattr(g, "headlabel");
  704. E_taillabel = agfindedgeattr(g, "taillabel");
  705. E_labelfontsize = agfindedgeattr(g, "labelfontsize");
  706. E_labelfontname = agfindedgeattr(g, "labelfontname");
  707. E_labelfontcolor = agfindedgeattr(g, "labelfontcolor");
  708. E_labeldistance = agfindedgeattr(g, "labeldistance");
  709. E_labelangle = agfindedgeattr(g, "labelangle");
  710. E_minlen = agfindedgeattr(g, "minlen");
  711. E_showboxes = agfindedgeattr(g, "showboxes");
  712. E_style = agfindedgeattr(g, "style");
  713. E_decorate = agfindedgeattr(g, "decorate");
  714. E_arrowsz = agfindedgeattr(g, "arrowsize");
  715. E_constr = agfindedgeattr(g, "constraint");
  716. E_layer = agfindedgeattr(g, "layer");
  717. E_comment = agfindedgeattr(g, "comment");
  718. E_tailclip = agfindedgeattr(g, "tailclip");
  719. E_headclip = agfindedgeattr(g, "headclip");
  720. E_penwidth = agfindedgeattr(g, "penwidth");
  721. /* background */
  722. GD_drawing(g)->xdots = init_xdot (g);
  723. /* initialize id, if any */
  724. if ((p = agget(g, "id")) && *p)
  725. GD_drawing(g)->id = strdup_and_subst_obj(p, g);
  726. }
  727. void graph_cleanup(graph_t *g)
  728. {
  729. if (GD_drawing(g) && GD_drawing(g)->xdots)
  730. freeXDot(GD_drawing(g)->xdots);
  731. if (GD_drawing(g))
  732. free (GD_drawing(g)->id);
  733. free(GD_drawing(g));
  734. GD_drawing(g) = NULL;
  735. free_label(GD_label(g));
  736. //FIX HERE , STILL SHALLOW
  737. //memset(&(g->u), 0, sizeof(Agraphinfo_t));
  738. agclean(g, AGRAPH,"Agraphinfo_t");
  739. }
  740. /// Given an internal charset value, return a canonical string representation.
  741. char*
  742. charsetToStr (int c)
  743. {
  744. char* s;
  745. switch (c) {
  746. case CHAR_UTF8 :
  747. s = "UTF-8";
  748. break;
  749. case CHAR_LATIN1 :
  750. s = "ISO-8859-1";
  751. break;
  752. case CHAR_BIG5 :
  753. s = "BIG-5";
  754. break;
  755. default :
  756. agerrorf("Unsupported charset value %d\n", c);
  757. s = "UTF-8";
  758. break;
  759. }
  760. return s;
  761. }
  762. /// Set characteristics of graph label if it exists.
  763. void do_graph_label(graph_t * sg)
  764. {
  765. char *str, *pos, *just;
  766. int pos_ix;
  767. /* it would be nice to allow multiple graph labels in the future */
  768. if ((str = agget(sg, "label")) && *str != '\0') {
  769. char pos_flag;
  770. pointf dimen;
  771. GD_has_labels(sg->root) |= GRAPH_LABEL;
  772. GD_label(sg) = make_label(sg, str, aghtmlstr(str) ? LT_HTML : LT_NONE,
  773. late_double(sg, agfindgraphattr(sg, "fontsize"),
  774. DEFAULT_FONTSIZE, MIN_FONTSIZE),
  775. late_nnstring(sg, agfindgraphattr(sg, "fontname"),
  776. DEFAULT_FONTNAME),
  777. late_nnstring(sg, agfindgraphattr(sg, "fontcolor"),
  778. DEFAULT_COLOR));
  779. /* set label position */
  780. pos = agget(sg, "labelloc");
  781. if (sg != agroot(sg)) {
  782. if (pos && pos[0] == 'b')
  783. pos_flag = LABEL_AT_BOTTOM;
  784. else
  785. pos_flag = LABEL_AT_TOP;
  786. } else {
  787. if (pos && pos[0] == 't')
  788. pos_flag = LABEL_AT_TOP;
  789. else
  790. pos_flag = LABEL_AT_BOTTOM;
  791. }
  792. just = agget(sg, "labeljust");
  793. if (just) {
  794. if (just[0] == 'l')
  795. pos_flag |= LABEL_AT_LEFT;
  796. else if (just[0] == 'r')
  797. pos_flag |= LABEL_AT_RIGHT;
  798. }
  799. GD_label_pos(sg) = pos_flag;
  800. if (sg == agroot(sg))
  801. return;
  802. /* Set border information for cluster labels to allow space
  803. */
  804. dimen = GD_label(sg)->dimen;
  805. PAD(dimen);
  806. if (!GD_flip(agroot(sg))) {
  807. if (GD_label_pos(sg) & LABEL_AT_TOP)
  808. pos_ix = TOP_IX;
  809. else
  810. pos_ix = BOTTOM_IX;
  811. GD_border(sg)[pos_ix] = dimen;
  812. } else {
  813. /* when rotated, the labels will be restored to TOP or BOTTOM */
  814. if (GD_label_pos(sg) & LABEL_AT_TOP)
  815. pos_ix = RIGHT_IX;
  816. else
  817. pos_ix = LEFT_IX;
  818. GD_border(sg)[pos_ix].x = dimen.y;
  819. GD_border(sg)[pos_ix].y = dimen.x;
  820. }
  821. }
  822. }