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 
21 static char *usageFmt =
22     "Usage: %s [-Vv?] [-(GNE)name=val] [-(KTlso)<val>] <dot files>\n";
23 
24 static 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 
40 static char *neatoFlags =
41     "(additional options for neato)    [-x] [-n<v>]\n";
42 static char *neatoItems = "\n\
43  -n[v]       - No layout mode 'v' (=1)\n\
44  -x          - Reduce graph\n";
45 
46 static char *fdpFlags =
47     "(additional options for fdp)      [-L(gO)] [-L(nUCT)<val>]\n";
48 static 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 
56 static char *memtestFlags = "(additional options for memtest)  [-m<v>]\n";
57 static 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 
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 
67 /* dotneato_usage:
68  * Print usage information. If GvExitOnUsage is set, exit with
69  * given exval, else return exval+1.
70  */
dotneato_usage(int exval)71 int 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  */
getFlagOpt(int argc,char ** argv,int * idx)104 static 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  */
dotneato_basename(char * path)128 static 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 
use_library(GVC_t * gvc,const char * name)166 static 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 
global_def(agxbuf * xb,char * dcl,int kind,attrsym_t * ((* dclfun)(Agraph_t *,int kind,char *,char *)))177 static 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 
gvg_init(GVC_t * gvc,graph_t * g,char * fn,int gidx)194 static 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 
211 static graph_t *P_graph;
212 
gvPluginsGraph(GVC_t * gvc)213 graph_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  */
dotneato_args_initialize(GVC_t * gvc,int argc,char ** argv)224 int 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  */
getdoubles2ptf(graph_t * g,char * name,pointf * result)447 static 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 
getdouble(graph_t * g,char * name,double * result)475 void 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  */
myfgets(char * ubuf,int n,FILE * fp)499 static 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 
gvNextInputGraph(GVC_t * gvc)537 graph_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  */
findCharset(graph_t * g)586 static 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  */
setRatio(graph_t * g)616 static 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 */
graph_init(graph_t * g,boolean use_rankdir)654 void 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     /* reparseable input */
666     if ((p = agget(g, "postaction"))) {   /* requires a graph wrapper for yyparse */
667         char *buf = gmalloc(strlen("digraph {  }") + strlen(p) + 1);
668         sprintf(buf,"%s { %s }",agisdirected(g)?"digraph":"graph",p);
669         agmemconcat(g, buf);
670     }
671 
672     /* set this up fairly early in case any string sizes are needed */
673     if ((p = agget(g, "fontpath")) || (p = getenv("DOTFONTPATH"))) {
674 	/* overide GDFONTPATH in local environment if dot
675 	 * wants its own */
676 #ifdef HAVE_SETENV
677 	setenv("GDFONTPATH", p, 1);
678 #else
679 	static char *buf = 0;
680 
681 	buf = grealloc(buf, strlen("GDFONTPATH=") + strlen(p) + 1);
682 	strcpy(buf, "GDFONTPATH=");
683 	strcat(buf, p);
684 	putenv(buf);
685 #endif
686     }
687 
688     GD_charset(g) = findCharset (g);
689 
690     if (!HTTPServerEnVar) {
691 	Gvimagepath = agget (g, "imagepath");
692 	if (!Gvimagepath)
693 	    Gvimagepath = Gvfilepath;
694     }
695 
696     GD_drawing(g)->quantum =
697 	late_double(g, agfindgraphattr(g, "quantum"), 0.0, 0.0);
698 
699     /* setting rankdir=LR is only defined in dot,
700      * but having it set causes shape code and others to use it.
701      * The result is confused output, so we turn it off unless requested.
702      * This effective rankdir is stored in the bottom 2 bits of g->u.rankdir.
703      * Sometimes, the code really needs the graph's rankdir, e.g., neato -n
704      * with record shapes, so we store the real rankdir in the next 2 bits.
705      */
706     rankdir = RANKDIR_TB;
707     if ((p = agget(g, "rankdir"))) {
708 	if (streq(p, "LR"))
709 	    rankdir = RANKDIR_LR;
710 	else if (streq(p, "BT"))
711 	    rankdir = RANKDIR_BT;
712 	else if (streq(p, "RL"))
713 	    rankdir = RANKDIR_RL;
714     }
715     if (use_rankdir)
716 	SET_RANKDIR (g, (rankdir << 2) | rankdir);
717     else
718 	SET_RANKDIR (g, (rankdir << 2));
719 
720     xf = late_double(g, agfindgraphattr(g, "nodesep"),
721 		DEFAULT_NODESEP, MIN_NODESEP);
722     GD_nodesep(g) = POINTS(xf);
723 
724     p = late_string(g, agfindgraphattr(g, "ranksep"), NULL);
725     if (p) {
726 	if (sscanf(p, "%lf", &xf) == 0)
727 	    xf = DEFAULT_RANKSEP;
728 	else {
729 	    if (xf < MIN_RANKSEP)
730 		xf = MIN_RANKSEP;
731 	}
732 	if (strstr(p, "equally"))
733 	    GD_exact_ranksep(g) = TRUE;
734     } else
735 	xf = DEFAULT_RANKSEP;
736     GD_ranksep(g) = POINTS(xf);
737 
738     GD_showboxes(g) = late_int(g, agfindgraphattr(g, "showboxes"), 0, 0);
739     p = late_string(g, agfindgraphattr(g, "fontnames"), NULL);
740     GD_fontnames(g) = maptoken(p, fontnamenames, fontnamecodes);
741 
742     setRatio(g);
743     GD_drawing(g)->filled =
744 	getdoubles2ptf(g, "size", &(GD_drawing(g)->size));
745     getdoubles2ptf(g, "page", &(GD_drawing(g)->page));
746 
747     GD_drawing(g)->centered = mapbool(agget(g, "center"));
748 
749     if ((p = agget(g, "rotate")))
750 	GD_drawing(g)->landscape = (atoi(p) == 90);
751     else if ((p = agget(g, "orientation")))
752 	GD_drawing(g)->landscape = ((p[0] == 'l') || (p[0] == 'L'));
753     else if ((p = agget(g, "landscape")))
754 	GD_drawing(g)->landscape = mapbool(p);
755 
756     p = agget(g, "clusterrank");
757     CL_type = maptoken(p, rankname, rankcode);
758     p = agget(g, "concentrate");
759     Concentrate = mapbool(p);
760     State = GVBEGIN;
761     EdgeLabelsDone = 0;
762 
763     GD_drawing(g)->dpi = 0.0;
764     if (((p = agget(g, "dpi")) && p[0])
765 	|| ((p = agget(g, "resolution")) && p[0]))
766 	GD_drawing(g)->dpi = atof(p);
767 
768     do_graph_label(g);
769 
770     Initial_dist = MYHUGE;
771 
772     G_ordering = agfindgraphattr(g, "ordering");
773     G_gradientangle = agfindgraphattr(g,"gradientangle");
774     G_margin = agfindgraphattr(g, "margin");
775 
776     /* initialize nodes */
777     N_height = agfindnodeattr(g, "height");
778     N_width = agfindnodeattr(g, "width");
779     N_shape = agfindnodeattr(g, "shape");
780     N_color = agfindnodeattr(g, "color");
781     N_fillcolor = agfindnodeattr(g, "fillcolor");
782     N_style = agfindnodeattr(g, "style");
783     N_fontsize = agfindnodeattr(g, "fontsize");
784     N_fontname = agfindnodeattr(g, "fontname");
785     N_fontcolor = agfindnodeattr(g, "fontcolor");
786     N_label = agfindnodeattr(g, "label");
787     if (!N_label)
788 	N_label = agattr(g, AGNODE, "label", NODENAME_ESC);
789     N_xlabel = agfindnodeattr(g, "xlabel");
790     N_showboxes = agfindnodeattr(g, "showboxes");
791     N_penwidth = agfindnodeattr(g, "penwidth");
792     N_ordering = agfindnodeattr(g, "ordering");
793     N_margin = agfindnodeattr(g, "margin");
794     /* attribs for polygon shapes */
795     N_sides = agfindnodeattr(g, "sides");
796     N_peripheries = agfindnodeattr(g, "peripheries");
797     N_skew = agfindnodeattr(g, "skew");
798     N_orientation = agfindnodeattr(g, "orientation");
799     N_distortion = agfindnodeattr(g, "distortion");
800     N_fixed = agfindnodeattr(g, "fixedsize");
801     N_imagescale = agfindnodeattr(g, "imagescale");
802     N_imagepos = agfindnodeattr(g, "imagepos");
803     N_nojustify = agfindnodeattr(g, "nojustify");
804     N_layer = agfindnodeattr(g, "layer");
805     N_group = agfindnodeattr(g, "group");
806     N_comment = agfindnodeattr(g, "comment");
807     N_vertices = agfindnodeattr(g, "vertices");
808     N_z = agfindnodeattr(g, "z");
809     N_gradientangle = agfindnodeattr(g,"gradientangle");
810 
811     /* initialize edges */
812     E_weight = agfindedgeattr(g, "weight");
813     E_color = agfindedgeattr(g, "color");
814     E_fillcolor = agfindedgeattr(g, "fillcolor");
815     E_fontsize = agfindedgeattr(g, "fontsize");
816     E_fontname = agfindedgeattr(g, "fontname");
817     E_fontcolor = agfindedgeattr(g, "fontcolor");
818     E_label = agfindedgeattr(g, "label");
819     E_xlabel = agfindedgeattr(g, "xlabel");
820     E_label_float = agfindedgeattr(g, "labelfloat");
821     /* vladimir */
822     E_dir = agfindedgeattr(g, "dir");
823     E_arrowhead = agfindedgeattr(g, "arrowhead");
824     E_arrowtail = agfindedgeattr(g, "arrowtail");
825     E_headlabel = agfindedgeattr(g, "headlabel");
826     E_taillabel = agfindedgeattr(g, "taillabel");
827     E_labelfontsize = agfindedgeattr(g, "labelfontsize");
828     E_labelfontname = agfindedgeattr(g, "labelfontname");
829     E_labelfontcolor = agfindedgeattr(g, "labelfontcolor");
830     E_labeldistance = agfindedgeattr(g, "labeldistance");
831     E_labelangle = agfindedgeattr(g, "labelangle");
832     /* end vladimir */
833     E_minlen = agfindedgeattr(g, "minlen");
834     E_showboxes = agfindedgeattr(g, "showboxes");
835     E_style = agfindedgeattr(g, "style");
836     E_decorate = agfindedgeattr(g, "decorate");
837     E_arrowsz = agfindedgeattr(g, "arrowsize");
838     E_constr = agfindedgeattr(g, "constraint");
839     E_layer = agfindedgeattr(g, "layer");
840     E_comment = agfindedgeattr(g, "comment");
841     E_tailclip = agfindedgeattr(g, "tailclip");
842     E_headclip = agfindedgeattr(g, "headclip");
843     E_penwidth = agfindedgeattr(g, "penwidth");
844 
845     /* background */
846     GD_drawing(g)->xdots = init_xdot (g);
847 
848     /* initialize id, if any */
849     if ((p = agget(g, "id")) && *p)
850 	GD_drawing(g)->id = strdup_and_subst_obj(p, g);
851 }
852 
graph_cleanup(graph_t * g)853 void graph_cleanup(graph_t *g)
854 {
855     if (GD_drawing(g) && GD_drawing(g)->xdots)
856 	freeXDot ((xdot*)GD_drawing(g)->xdots);
857     if (GD_drawing(g) && GD_drawing(g)->id)
858 	free (GD_drawing(g)->id);
859     free(GD_drawing(g));
860     GD_drawing(g) = NULL;
861     free_label(GD_label(g));
862     //FIX HERE , STILL SHALLOW
863     //memset(&(g->u), 0, sizeof(Agraphinfo_t));
864     agclean(g, AGRAPH,"Agraphinfo_t");
865 }
866 
867 /* charsetToStr:
868  * Given an internal charset value, return a canonical string
869  * representation.
870  */
871 char*
charsetToStr(int c)872 charsetToStr (int c)
873 {
874    char* s;
875 
876    switch (c) {
877    case CHAR_UTF8 :
878 	s = "UTF-8";
879 	break;
880    case CHAR_LATIN1 :
881 	s = "ISO-8859-1";
882 	break;
883    case CHAR_BIG5 :
884 	s = "BIG-5";
885 	break;
886    default :
887 	agerr(AGERR, "Unsupported charset value %d\n", c);
888 	s = "UTF-8";
889 	break;
890    }
891    return s;
892 }
893 
894 /* do_graph_label:
895  * Set characteristics of graph label if it exists.
896  *
897  */
do_graph_label(graph_t * sg)898 void do_graph_label(graph_t * sg)
899 {
900     char *str, *pos, *just;
901     int pos_ix;
902 
903     /* it would be nice to allow multiple graph labels in the future */
904     if ((str = agget(sg, "label")) && (*str != '\0')) {
905 	char pos_flag;
906 	pointf dimen;
907 
908 	GD_has_labels(sg->root) |= GRAPH_LABEL;
909 
910 	GD_label(sg) = make_label((void*)sg, str, (aghtmlstr(str) ? LT_HTML : LT_NONE),
911 	    late_double(sg, agfindgraphattr(sg, "fontsize"),
912 			DEFAULT_FONTSIZE, MIN_FONTSIZE),
913 	    late_nnstring(sg, agfindgraphattr(sg, "fontname"),
914 			DEFAULT_FONTNAME),
915 	    late_nnstring(sg, agfindgraphattr(sg, "fontcolor"),
916 			DEFAULT_COLOR));
917 
918 	/* set label position */
919 	pos = agget(sg, "labelloc");
920 	if (sg != agroot(sg)) {
921 	    if (pos && (pos[0] == 'b'))
922 		pos_flag = LABEL_AT_BOTTOM;
923 	    else
924 		pos_flag = LABEL_AT_TOP;
925 	} else {
926 	    if (pos && (pos[0] == 't'))
927 		pos_flag = LABEL_AT_TOP;
928 	    else
929 		pos_flag = LABEL_AT_BOTTOM;
930 	}
931 	just = agget(sg, "labeljust");
932 	if (just) {
933 	    if (just[0] == 'l')
934 		pos_flag |= LABEL_AT_LEFT;
935 	    else if (just[0] == 'r')
936 		pos_flag |= LABEL_AT_RIGHT;
937 	}
938 	GD_label_pos(sg) = pos_flag;
939 
940 	if (sg == agroot(sg))
941 	    return;
942 
943 	/* Set border information for cluster labels to allow space
944 	 */
945 	dimen = GD_label(sg)->dimen;
946 	PAD(dimen);
947 	if (!GD_flip(agroot(sg))) {
948 	    if (GD_label_pos(sg) & LABEL_AT_TOP)
949 		pos_ix = TOP_IX;
950 	    else
951 		pos_ix = BOTTOM_IX;
952 	    GD_border(sg)[pos_ix] = dimen;
953 	} else {
954 	    /* when rotated, the labels will be restored to TOP or BOTTOM  */
955 	    if (GD_label_pos(sg) & LABEL_AT_TOP)
956 		pos_ix = RIGHT_IX;
957 	    else
958 		pos_ix = LEFT_IX;
959 	    GD_border(sg)[pos_ix].x = dimen.y;
960 	    GD_border(sg)[pos_ix].y = dimen.x;
961 	}
962     }
963 }
964