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