Graphviz  2.35.20130930.0449
input.c
Go to the documentation of this file.
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  */
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  */
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  */
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++; s--;
144  /* skip over trailing slashes, nulling out as we go */
145  while ((s > path) && ((*s == '/') || (*s == '\\')))
146  *s-- = '\0';
147  if (s == path) ret = path;
148  else {
149  while ((s > path) && ((*s != '/') && (*s != '\\'))) s--;
150  if ((*s == '/') || (*s == '\\')) ret = s+1;
151  else ret = path;
152  }
153 #ifdef WIN32
154  /* On Windows, names are case-insensitive, so make name lower-case
155  */
156  {
157  char c;
158  for (s = ret; (c = *s); s++)
159  *s = tolower(c);
160  }
161 #endif
162  return ret;
163 }
164 
165 static void use_library(GVC_t *gvc, const char *name)
166 {
167  static int cnt = 0;
168  if (name) {
169  Lib = ALLOC(cnt + 2, Lib, const char *);
170  Lib[cnt++] = name;
171  Lib[cnt] = NULL;
172  }
173  gvc->common.lib = Lib;
174 }
175 
176 #ifdef WITH_CGRAPH
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 #else
194 static void global_def(agxbuf* xb, char *dcl,
195  attrsym_t * ((*dclfun) (Agraph_t *, char *, char *)))
196 {
197  char *p;
198  char *rhs = "true";
199 
200  attrsym_t *sym;
201  if ((p = strchr(dcl, '='))) {
202  agxbput_n (xb, dcl, p-dcl);
203  rhs = p+1;
204  }
205  else
206  agxbput (xb, dcl);
207  sym = dclfun(NULL, agxbuse (xb), rhs);
208  sym->fixed = 1;
209 }
210 #endif
211 
212 static int gvg_init(GVC_t *gvc, graph_t *g, char *fn, int gidx)
213 {
214  GVG_t *gvg;
215 
216  gvg = zmalloc(sizeof(GVG_t));
217  if (!gvc->gvgs)
218  gvc->gvgs = gvg;
219  else
220  gvc->gvg->next = gvg;
221  gvc->gvg = gvg;
222  gvg->gvc = gvc;
223  gvg->g = g;
224  gvg->input_filename = fn;
225  gvg->graph_index = gidx;
226  return 0;
227 }
228 
229 static graph_t *P_graph;
230 
232 {
233  gvg_init(gvc, P_graph, "<internal>", 0);
234  return P_graph;
235 }
236 
237 /* dotneato_args_initialize"
238  * Scan argv[] for allowed flags.
239  * Return 0 on success; v+1 if calling function should call exit(v).
240  * If -c is set, config file is created and we exit.
241  */
242 int dotneato_args_initialize(GVC_t * gvc, int argc, char **argv)
243 {
244  char c, *rest, *layout;
245  const char *val;
246  int i, v, nfiles;
247  unsigned char buf[SMALLBUF];
248  agxbuf xb;
249  int Kflag = 0;
250 
251  /* establish if we are running in a CGI environment */
252  HTTPServerEnVar = getenv("SERVER_NAME");
253 
254  /* establish Gvfilepath, if any */
255  Gvfilepath = getenv("GV_FILE_PATH");
256 
257  gvc->common.cmdname = dotneato_basename(argv[0]);
258  if (gvc->common.verbose) {
259  fprintf(stderr, "%s - %s version %s (%s)\n",
260  gvc->common.cmdname, gvc->common.info[0],
261  gvc->common.info[1], gvc->common.info[2]);
262  }
263 
264  /* configure for available plugins */
265  /* needs to know if "dot -c" is set (gvc->common.config) */
266  /* must happen before trying to select any plugins */
267  gvconfig(gvc, gvc->common.config);
268  if (gvc->common.config)
269  exit (0);
270 
271  /* feed the globals */
272  Verbose = gvc->common.verbose;
273  CmdName = gvc->common.cmdname;
274 
275 #ifndef WITH_CGRAPH
276  aginit();
277 #endif
278  nfiles = 0;
279  for (i = 1; i < argc; i++)
280  if (argv[i] && argv[i][0] != '-')
281  nfiles++;
282  gvc->input_filenames = N_NEW(nfiles + 1, char *);
283  nfiles = 0;
284  agxbinit(&xb, SMALLBUF, buf);
285  for (i = 1; i < argc; i++) {
286  if (argv[i] && argv[i][0] == '-') {
287  rest = &(argv[i][2]);
288  switch (c = argv[i][1]) {
289  case 'G':
290  if (*rest)
291 #ifdef WITH_CGRAPH
292  global_def(&xb, rest, AGRAPH, agattr);
293 #else
294  global_def(&xb, rest, agraphattr);
295 #endif
296  else {
297  fprintf(stderr, "Missing argument for -G flag\n");
298  return (dotneato_usage(1));
299  }
300  break;
301  case 'N':
302  if (*rest)
303 #ifdef WITH_CGRAPH
304  global_def(&xb, rest, AGNODE,agattr);
305 #else
306  global_def(&xb, rest, agnodeattr);
307 #endif
308  else {
309  fprintf(stderr, "Missing argument for -N flag\n");
310  return (dotneato_usage(1));
311  }
312  break;
313  case 'E':
314  if (*rest)
315 #ifdef WITH_CGRAPH
316  global_def(&xb, rest, AGEDGE,agattr);
317 #else
318  global_def(&xb, rest, agedgeattr);
319 #endif
320  else {
321  fprintf(stderr, "Missing argument for -E flag\n");
322  return (dotneato_usage(1));
323  }
324  break;
325  case 'T':
326  val = getFlagOpt(argc, argv, &i);
327  if (!val) {
328  fprintf(stderr, "Missing argument for -T flag\n");
329  return (dotneato_usage(1));
330  }
331  v = gvjobs_output_langname(gvc, val);
332  if (!v) {
333  fprintf(stderr, "Format: \"%s\" not recognized. Use one of:%s\n",
334  val, gvplugin_list(gvc, API_device, val));
335  if (GvExitOnUsage) exit(1);
336  return(2);
337  }
338  break;
339  case 'K':
340  val = getFlagOpt(argc, argv, &i);
341  if (!val) {
342  fprintf(stderr, "Missing argument for -K flag\n");
343  return (dotneato_usage(1));
344  }
345  v = gvlayout_select(gvc, val);
346  if (v == NO_SUPPORT) {
347  fprintf(stderr, "There is no layout engine support for \"%s\"\n", val);
348  if (streq(val, "dot")) {
349  fprintf(stderr, "Perhaps \"dot -c\" needs to be run (with installer's privileges) to register the plugins?\n");
350  }
351  else {
352  fprintf(stderr, "Use one of:%s\n",
353  gvplugin_list(gvc, API_layout, val));
354  }
355  if (GvExitOnUsage) exit(1);
356  return(2);
357  }
358  Kflag = 1;
359  break;
360  case 'P':
361  P_graph = gvplugin_graph(gvc);
362  break;
363  case 'V':
364  fprintf(stderr, "%s - %s version %s (%s)\n",
365  gvc->common.cmdname, gvc->common.info[0],
366  gvc->common.info[1], gvc->common.info[2]);
367  if (GvExitOnUsage) exit(0);
368  return (1);
369  break;
370  case 'l':
371  val = getFlagOpt(argc, argv, &i);
372  if (!val) {
373  fprintf(stderr, "Missing argument for -l flag\n");
374  return (dotneato_usage(1));
375  }
376  use_library(gvc, val);
377  break;
378  case 'o':
379  val = getFlagOpt(argc, argv, &i);
380  if (! gvc->common.auto_outfile_names)
381  gvjobs_output_filename(gvc, val);
382  break;
383  case 'q':
384  if (*rest) {
385  v = atoi(rest);
386  if (v <= 0) {
387  fprintf(stderr,
388  "Invalid parameter \"%s\" for -q flag - ignored\n",
389  rest);
390  } else if (v == 1)
391  agseterr(AGERR);
392  else
393  agseterr(AGMAX);
394  } else
395  agseterr(AGERR);
396  break;
397  case 's':
398  if (*rest) {
399  PSinputscale = atof(rest);
400  if (PSinputscale <= 0) {
401  fprintf(stderr,
402  "Invalid parameter \"%s\" for -s flag\n",
403  rest);
404  return (dotneato_usage(1));
405  }
406  } else
407  PSinputscale = POINTS_PER_INCH;
408  break;
409  case 'x':
410  Reduce = TRUE;
411  break;
412  case 'y':
413  Y_invert = TRUE;
414  break;
415  case '?':
416  return (dotneato_usage(0));
417  break;
418  default:
419  agerr(AGERR, "%s: option -%c unrecognized\n\n", gvc->common.cmdname,
420  c);
421  return (dotneato_usage(1));
422  }
423  } else if (argv[i])
424  gvc->input_filenames[nfiles++] = argv[i];
425  }
426  agxbfree (&xb);
427 
428  /* if no -K, use cmd name to set layout type */
429  if (!Kflag) {
430  layout = gvc->common.cmdname;
431  if (streq(layout, "dot_static")
432  || streq(layout, "dot_builtins")
433  || streq(layout, "lt-dot")
434  || streq(layout, "lt-dot_builtins")
435  || streq(layout, "") /* when run as a process from Gvedit on Windows */
436  )
437  layout = "dot";
438  i = gvlayout_select(gvc, layout);
439  if (i == NO_SUPPORT) {
440  fprintf(stderr, "There is no layout engine support for \"%s\"\n", layout);
441  if (streq(layout, "dot"))
442  fprintf(stderr, "Perhaps \"dot -c\" needs to be run (with installer's privileges) to register the plugins?\n");
443  else
444  fprintf(stderr, "Use one of:%s\n", gvplugin_list(gvc, API_layout, ""));
445 
446  if (GvExitOnUsage) exit(1);
447  return(2);
448  }
449  }
450 
451  /* if no -Txxx, then set default format */
452  if (!gvc->jobs || !gvc->jobs->output_langname) {
453  v = gvjobs_output_langname(gvc, "dot");
454  if (!v) {
455 // assert(v); /* "dot" should always be available as an output format */
456  fprintf(stderr,
457  "Unable to find even the default \"-Tdot\" renderer. Has the config\nfile been generated by running \"dot -c\" with installer's priviledges?\n");
458  return(2);
459  }
460  }
461 
462  /* set persistent attributes here (if not already set from command line options) */
463 #ifdef WITH_CGRAPH
464  if (!agattr(NULL, AGNODE, "label", 0))
465  agattr(NULL, AGNODE, "label", NODENAME_ESC);
466 #else
467  if (!(agfindnodeattr(agprotograph(), "label")))
468  agnodeattr(NULL, "label", NODENAME_ESC);
469 #endif
470  return 0;
471 }
472 
473 /* getdoubles2ptf:
474  * converts a graph attribute in inches to a pointf in points.
475  * If only one number is given, it is used for both x and y.
476  * Returns true if the attribute ends in '!'.
477  */
478 static boolean getdoubles2ptf(graph_t * g, char *name, pointf * result)
479 {
480  char *p;
481  int i;
482  double xf, yf;
483  char c = '\0';
484  boolean rv = FALSE;
485 
486  if ((p = agget(g, name))) {
487  i = sscanf(p, "%lf,%lf%c", &xf, &yf, &c);
488  if ((i > 1) && (xf > 0) && (yf > 0)) {
489  result->x = POINTS(xf);
490  result->y = POINTS(yf);
491  if (c == '!')
492  rv = TRUE;
493  }
494  else {
495  c = '\0';
496  i = sscanf(p, "%lf%c", &xf, &c);
497  if ((i > 0) && (xf > 0)) {
498  result->y = result->x = POINTS(xf);
499  if (c == '!') rv = TRUE;
500  }
501  }
502  }
503  return rv;
504 }
505 
506 void getdouble(graph_t * g, char *name, double *result)
507 {
508  char *p;
509  double f;
510 
511  if ((p = agget(g, name))) {
512  if (sscanf(p, "%lf", &f) >= 1)
513  *result = f;
514  }
515 }
516 
517 #ifdef EXPERIMENTAL_MYFGETS
518 /*
519  * Potential input filter - e.g. for iconv
520  */
521 
522 /*
523  * myfgets - same api as fgets
524  *
525  * gets n chars at a time
526  *
527  * returns pointer to user buffer,
528  * or returns NULL on eof or error.
529  */
530 static char *myfgets(char * ubuf, int n, FILE * fp)
531 {
532  static char *buf;
533  static int bufsz, pos, len;
534  int cnt;
535 
536  if (!n) { /* a call with n==0 (from aglexinit) resets */
537  ubuf[0] = '\0';
538  pos = len = 0;
539  return NULL;
540  }
541 
542  if (!len) {
543  if (n > bufsz) {
544  bufsz = n;
545  buf = realloc(buf, bufsz);
546  }
547  if (!(fgets(buf, bufsz, fp))) {
548  ubuf[0] = '\0';
549  return NULL;
550  }
551  len = strlen(buf);
552  pos = 0;
553  }
554 
555  cnt = n - 1;
556  if (len < cnt)
557  cnt = len;
558 
559  memcpy(ubuf, buf + pos, cnt);
560  pos += cnt;
561  len -= cnt;
562  ubuf[cnt] = '\0';
563 
564  return ubuf;
565 }
566 #endif
567 
569 {
570  graph_t *g = NULL;
571  static char *fn;
572  static FILE *fp;
573  static FILE *oldfp;
574  static int fidx, gidx;
575 
576  while (!g) {
577  if (!fp) {
578  if (!(fn = gvc->input_filenames[0])) {
579  if (fidx++ == 0)
580  fp = stdin;
581  }
582  else {
583  while ((fn = gvc->input_filenames[fidx++]) && !(fp = fopen(fn, "r"))) {
584  agerr(AGERR, "%s: can't open %s\n", gvc->common.cmdname, fn);
585  graphviz_errors++;
586  }
587  }
588  }
589  if (fp == NULL)
590  break;
591  if (oldfp != fp) {
592  agsetfile(fn ? fn : "<stdin>");
593  oldfp = fp;
594  }
595 #ifdef EXPERIMENTAL_MYFGETS
596  g = agread_usergets(fp, myfgets);
597 #else
598 #ifdef WITH_CGRAPH
599  g = agread(fp,NIL(Agdisc_t*));
600 #else
601  g = agread(fp);
602 #endif
603 #endif
604  if (g) {
605  gvg_init(gvc, g, fn, gidx++);
606  break;
607  }
608  if (fp != stdin)
609  fclose (fp);
610  fp = NULL;
611  gidx = 0;
612  }
613  return g;
614 }
615 
616 /* findCharset:
617  * Check if the charset attribute is defined for the graph and, if
618  * so, return the corresponding internal value. If undefined, return
619  * CHAR_UTF8
620  */
621 static int findCharset (graph_t * g)
622 {
623  int enc;
624  char* p;
625 
626  p = late_nnstring(g,agfindgraphattr(g,"charset"),"utf-8");
627  if (!strcasecmp(p,"latin-1")
628  || !strcasecmp(p,"latin1")
629  || !strcasecmp(p,"l1")
630  || !strcasecmp(p,"ISO-8859-1")
631  || !strcasecmp(p,"ISO_8859-1")
632  || !strcasecmp(p,"ISO8859-1")
633  || !strcasecmp(p,"ISO-IR-100"))
634  enc = CHAR_LATIN1;
635  else if (!strcasecmp(p,"big-5")
636  || !strcasecmp(p,"big5"))
637  enc = CHAR_BIG5;
638  else if (!strcasecmp(p,"utf-8")
639  || !strcasecmp(p,"utf8"))
640  enc = CHAR_UTF8;
641  else {
642  agerr(AGWARN, "Unsupported charset \"%s\" - assuming utf-8\n", p);
643  enc = CHAR_UTF8;
644  }
645  return enc;
646 }
647 
648 /* setRatio:
649  * Checks "ratio" attribute, if any, and sets enum type.
650  */
651 static void setRatio(graph_t * g)
652 {
653  char *p, c;
654  double ratio;
655 
656  if ((p = agget(g, "ratio")) && ((c = p[0]))) {
657  switch (c) {
658  case 'a':
659  if (streq(p, "auto"))
660  GD_drawing(g)->ratio_kind = R_AUTO;
661  break;
662  case 'c':
663  if (streq(p, "compress"))
664  GD_drawing(g)->ratio_kind = R_COMPRESS;
665  break;
666  case 'e':
667  if (streq(p, "expand"))
668  GD_drawing(g)->ratio_kind = R_EXPAND;
669  break;
670  case 'f':
671  if (streq(p, "fill"))
672  GD_drawing(g)->ratio_kind = R_FILL;
673  break;
674  default:
675  ratio = atof(p);
676  if (ratio > 0.0) {
677  GD_drawing(g)->ratio_kind = R_VALUE;
678  GD_drawing(g)->ratio = ratio;
679  }
680  break;
681  }
682  }
683 }
684 
685 /*
686  cgraph requires
687 
688 */
689 void graph_init(graph_t * g, boolean use_rankdir)
690 {
691  char *p;
692  double xf;
693  static char *rankname[] = { "local", "global", "none", NULL };
694  static int rankcode[] = { LOCAL, GLOBAL, NOCLUST, LOCAL };
695  static char *fontnamenames[] = {"gd","ps","svg", NULL};
696  static int fontnamecodes[] = {NATIVEFONTS,PSFONTS,SVGFONTS,-1};
697  int rankdir;
698  GD_drawing(g) = NEW(layout_t);
699 
700  /* set this up fairly early in case any string sizes are needed */
701  if ((p = agget(g, "fontpath")) || (p = getenv("DOTFONTPATH"))) {
702  /* overide GDFONTPATH in local environment if dot
703  * wants its own */
704 #ifdef HAVE_SETENV
705  setenv("GDFONTPATH", p, 1);
706 #else
707  static char *buf = 0;
708 
709  buf = grealloc(buf, strlen("GDFONTPATH=") + strlen(p) + 1);
710  strcpy(buf, "GDFONTPATH=");
711  strcat(buf, p);
712  putenv(buf);
713 #endif
714  }
715 
716  GD_charset(g) = findCharset (g);
717 
718  if (!HTTPServerEnVar) {
719  Gvimagepath = agget (g, "imagepath");
720  if (!Gvimagepath)
722  }
723 
724  GD_drawing(g)->quantum =
725  late_double(g, agfindgraphattr(g, "quantum"), 0.0, 0.0);
726 
727  /* setting rankdir=LR is only defined in dot,
728  * but having it set causes shape code and others to use it.
729  * The result is confused output, so we turn it off unless requested.
730  * This effective rankdir is stored in the bottom 2 bits of g->u.rankdir.
731  * Sometimes, the code really needs the graph's rankdir, e.g., neato -n
732  * with record shapes, so we store the real rankdir in the next 2 bits.
733  */
734  rankdir = RANKDIR_TB;
735  if ((p = agget(g, "rankdir"))) {
736  if (streq(p, "LR"))
737  rankdir = RANKDIR_LR;
738  else if (streq(p, "BT"))
739  rankdir = RANKDIR_BT;
740  else if (streq(p, "RL"))
741  rankdir = RANKDIR_RL;
742  }
743  if (use_rankdir)
744  SET_RANKDIR (g, (rankdir << 2) | rankdir);
745  else
746  SET_RANKDIR (g, (rankdir << 2));
747 
748  xf = late_double(g, agfindgraphattr(g, "nodesep"),
750  GD_nodesep(g) = POINTS(xf);
751 
752  p = late_string(g, agfindgraphattr(g, "ranksep"), NULL);
753  if (p) {
754  if (sscanf(p, "%lf", &xf) == 0)
755  xf = DEFAULT_RANKSEP;
756  else {
757  if (xf < MIN_RANKSEP)
758  xf = MIN_RANKSEP;
759  }
760  if (strstr(p, "equally"))
761  GD_exact_ranksep(g) = TRUE;
762  } else
763  xf = DEFAULT_RANKSEP;
764  GD_ranksep(g) = POINTS(xf);
765 
766  GD_showboxes(g) = late_int(g, agfindgraphattr(g, "showboxes"), 0, 0);
767  p = late_string(g, agfindgraphattr(g, "fontnames"), NULL);
768  GD_fontnames(g) = maptoken(p, fontnamenames, fontnamecodes);
769 
770  setRatio(g);
771  GD_drawing(g)->filled =
772  getdoubles2ptf(g, "size", &(GD_drawing(g)->size));
773  getdoubles2ptf(g, "page", &(GD_drawing(g)->page));
774 
775  GD_drawing(g)->centered = mapbool(agget(g, "center"));
776 
777  if ((p = agget(g, "rotate")))
778  GD_drawing(g)->landscape = (atoi(p) == 90);
779  else if ((p = agget(g, "orientation")))
780  GD_drawing(g)->landscape = ((p[0] == 'l') || (p[0] == 'L'));
781  else if ((p = agget(g, "landscape")))
782  GD_drawing(g)->landscape = mapbool(p);
783 
784  p = agget(g, "clusterrank");
785  CL_type = maptoken(p, rankname, rankcode);
786  p = agget(g, "concentrate");
787  Concentrate = mapbool(p);
788  State = GVBEGIN;
789  EdgeLabelsDone = 0;
790 
791  GD_drawing(g)->dpi = 0.0;
792  if (((p = agget(g, "dpi")) && p[0])
793  || ((p = agget(g, "resolution")) && p[0]))
794  GD_drawing(g)->dpi = atof(p);
795 
796  do_graph_label(g);
797 
799 
800  G_ordering = agfindgraphattr(g, "ordering");
801  G_gradientangle = agfindgraphattr(g,"gradientangle");
802  G_margin = agfindgraphattr(g, "margin");
803 
804  /* initialize nodes */
805  N_height = agfindnodeattr(g, "height");
806  N_width = agfindnodeattr(g, "width");
807  N_shape = agfindnodeattr(g, "shape");
808  N_color = agfindnodeattr(g, "color");
809  N_fillcolor = agfindnodeattr(g, "fillcolor");
810  N_style = agfindnodeattr(g, "style");
811  N_fontsize = agfindnodeattr(g, "fontsize");
812  N_fontname = agfindnodeattr(g, "fontname");
813  N_fontcolor = agfindnodeattr(g, "fontcolor");
814  N_label = agfindnodeattr(g, "label");
815  if (!N_label)
816 #ifdef WITH_CGRAPH
817  N_label = agattr(g, AGNODE, "label", NODENAME_ESC);
818 #else
819  N_label = agnodeattr(g, "label", NODENAME_ESC);
820 #endif
821  N_xlabel = agfindnodeattr(g, "xlabel");
822  N_showboxes = agfindnodeattr(g, "showboxes");
823  N_penwidth = agfindnodeattr(g, "penwidth");
824  N_ordering = agfindnodeattr(g, "ordering");
825  N_margin = agfindnodeattr(g, "margin");
826  /* attribs for polygon shapes */
827  N_sides = agfindnodeattr(g, "sides");
828  N_peripheries = agfindnodeattr(g, "peripheries");
829  N_skew = agfindnodeattr(g, "skew");
830  N_orientation = agfindnodeattr(g, "orientation");
831  N_distortion = agfindnodeattr(g, "distortion");
832  N_fixed = agfindnodeattr(g, "fixedsize");
833  N_imagescale = agfindnodeattr(g, "imagescale");
834  N_nojustify = agfindnodeattr(g, "nojustify");
835  N_layer = agfindnodeattr(g, "layer");
836  N_group = agfindnodeattr(g, "group");
837  N_comment = agfindnodeattr(g, "comment");
838  N_vertices = agfindnodeattr(g, "vertices");
839  N_z = agfindnodeattr(g, "z");
840  N_gradientangle = agfindnodeattr(g,"gradientangle");
841 
842  /* initialize edges */
843  E_weight = agfindedgeattr(g, "weight");
844  E_color = agfindedgeattr(g, "color");
845  E_fillcolor = agfindedgeattr(g, "fillcolor");
846  E_fontsize = agfindedgeattr(g, "fontsize");
847  E_fontname = agfindedgeattr(g, "fontname");
848  E_fontcolor = agfindedgeattr(g, "fontcolor");
849  E_label = agfindedgeattr(g, "label");
850  E_xlabel = agfindedgeattr(g, "xlabel");
851  E_label_float = agfindedgeattr(g, "labelfloat");
852  /* vladimir */
853  E_dir = agfindedgeattr(g, "dir");
854  E_arrowhead = agfindedgeattr(g, "arrowhead");
855  E_arrowtail = agfindedgeattr(g, "arrowtail");
856  E_headlabel = agfindedgeattr(g, "headlabel");
857  E_taillabel = agfindedgeattr(g, "taillabel");
858  E_labelfontsize = agfindedgeattr(g, "labelfontsize");
859  E_labelfontname = agfindedgeattr(g, "labelfontname");
860  E_labelfontcolor = agfindedgeattr(g, "labelfontcolor");
861  E_labeldistance = agfindedgeattr(g, "labeldistance");
862  E_labelangle = agfindedgeattr(g, "labelangle");
863  /* end vladimir */
864  E_minlen = agfindedgeattr(g, "minlen");
865  E_showboxes = agfindedgeattr(g, "showboxes");
866  E_style = agfindedgeattr(g, "style");
867  E_decorate = agfindedgeattr(g, "decorate");
868  E_arrowsz = agfindedgeattr(g, "arrowsize");
869  E_constr = agfindedgeattr(g, "constraint");
870  E_layer = agfindedgeattr(g, "layer");
871  E_comment = agfindedgeattr(g, "comment");
872  E_tailclip = agfindedgeattr(g, "tailclip");
873  E_headclip = agfindedgeattr(g, "headclip");
874  E_penwidth = agfindedgeattr(g, "penwidth");
875 
876  /* background */
877  GD_drawing(g)->xdots = init_xdot (g);
878 
879  /* initialize id, if any */
880 
881  if ((p = agget(g, "id")) && *p)
882  GD_drawing(g)->id = strdup_and_subst_obj(p, g);
883 }
884 
886 {
887  if (GD_drawing(g)->xdots)
888  freeXDot ((xdot*)GD_drawing(g)->xdots);
889  if (GD_drawing(g)->id)
890  free (GD_drawing(g)->id);
891  free(GD_drawing(g));
892  GD_drawing(g) = NULL;
893  free_label(GD_label(g));
894 #ifdef WITH_CGRAPH
895  //FIX HERE , STILL SHALLOW
896  //memset(&(g->u), 0, sizeof(Agraphinfo_t));
897  agclean(g, AGRAPH,"Agraphinfo_t");
898 #else
899  memset(&(g->u), 0, sizeof(Agraphinfo_t));
900 #endif
901 }
902 
903 /* charsetToStr:
904  * Given an internal charset value, return a canonical string
905  * representation.
906  */
907 char*
909 {
910  char* s;
911 
912  switch (c) {
913  case CHAR_UTF8 :
914  s = "UTF-8";
915  break;
916  case CHAR_LATIN1 :
917  s = "ISO-8859-1";
918  break;
919  case CHAR_BIG5 :
920  s = "BIG-5";
921  break;
922  default :
923  agerr(AGERR, "Unsupported charset value %d\n", c);
924  s = "UTF-8";
925  break;
926  }
927  return s;
928 }
929 
930 /* do_graph_label:
931  * Set characteristics of graph label if it exists.
932  *
933  */
935 {
936  char *str, *pos, *just;
937  int pos_ix;
938 
939  /* it would be nice to allow multiple graph labels in the future */
940  if ((str = agget(sg, "label")) && (*str != '\0')) {
941  char pos_flag;
942  pointf dimen;
943 
944  GD_has_labels(sg->root) |= GRAPH_LABEL;
945 
946  GD_label(sg) = make_label((void*)sg, str, (aghtmlstr(str) ? LT_HTML : LT_NONE),
947  late_double(sg, agfindgraphattr(sg, "fontsize"),
949  late_nnstring(sg, agfindgraphattr(sg, "fontname"),
951  late_nnstring(sg, agfindgraphattr(sg, "fontcolor"),
952  DEFAULT_COLOR));
953 
954  /* set label position */
955  pos = agget(sg, "labelloc");
956  if (sg != agroot(sg)) {
957  if (pos && (pos[0] == 'b'))
958  pos_flag = LABEL_AT_BOTTOM;
959  else
960  pos_flag = LABEL_AT_TOP;
961  } else {
962  if (pos && (pos[0] == 't'))
963  pos_flag = LABEL_AT_TOP;
964  else
965  pos_flag = LABEL_AT_BOTTOM;
966  }
967  just = agget(sg, "labeljust");
968  if (just) {
969  if (just[0] == 'l')
970  pos_flag |= LABEL_AT_LEFT;
971  else if (just[0] == 'r')
972  pos_flag |= LABEL_AT_RIGHT;
973  }
974  GD_label_pos(sg) = pos_flag;
975 
976  if (sg == agroot(sg))
977  return;
978 
979  /* Set border information for cluster labels to allow space
980  */
981  dimen = GD_label(sg)->dimen;
982  PAD(dimen);
983  if (!GD_flip(agroot(sg))) {
984  if (GD_label_pos(sg) & LABEL_AT_TOP)
985  pos_ix = TOP_IX;
986  else
987  pos_ix = BOTTOM_IX;
988  GD_border(sg)[pos_ix] = dimen;
989  } else {
990  /* when rotated, the labels will be restored to TOP or BOTTOM */
991  if (GD_label_pos(sg) & LABEL_AT_TOP)
992  pos_ix = RIGHT_IX;
993  else
994  pos_ix = LEFT_IX;
995  GD_border(sg)[pos_ix].x = dimen.y;
996  GD_border(sg)[pos_ix].y = dimen.x;
997  }
998  }
999 }