Graphviz  2.35.20130930.0449
gv.cpp
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 <string.h>
15 #include <stdlib.h>
16 #include "gvc.h"
17 
18 extern "C" {
19 extern void gv_string_writer_init(GVC_t *gvc);
20 extern void gv_channel_writer_init(GVC_t *gvc);
21 }
22 
23 #ifdef WITH_CGRAPH
24 #define agfindattr(x,s) agattrsym(x,s)
25 #define agraphattr(g,n,s) agattr(g,AGRAPH,n,s)
26 #define agnodeattr(g,n,s) agattr(g,AGNODE,n,s)
27 #define agedgeattr(g,n,s) agattr(g,AGEDGE,n,s)
28 #endif
29 
30 static char emptystring[] = {'\0'};
31 
32 static GVC_t *gvc;
33 
34 static void gv_init(void) {
35  /* list of builtins, enable demand loading */
36  gvc = gvContextPlugins(lt_preloaded_symbols, DEMAND_LOADING);
37 }
38 
39 Agraph_t *graph(char *name)
40 {
41  if (!gvc)
42  gv_init();
43 #ifdef WITH_CGRAPH
44  return agopen(name, Agundirected, 0);
45 #else
46  return agopen(name, AGRAPH);
47 #endif
48 }
49 
50 Agraph_t *digraph(char *name)
51 {
52  if (!gvc)
53  gv_init();
54 #ifdef WITH_CGRAPH
55  return agopen(name, Agdirected, 0);
56 #else
57  return agopen(name, AGDIGRAPH);
58 #endif
59 }
60 
61 Agraph_t *strictgraph(char *name)
62 {
63  if (!gvc)
64  gv_init();
65 #ifdef WITH_CGRAPH
66  return agopen(name, Agstrictundirected, 0);
67 #else
68  return agopen(name, AGRAPHSTRICT);
69 #endif
70 }
71 
72 Agraph_t *strictdigraph(char *name)
73 {
74  if (!gvc)
75  gv_init();
76 #ifdef WITH_CGRAPH
77  return agopen(name, Agstrictdirected, 0);
78 #else
79  return agopen(name, AGDIGRAPHSTRICT);
80 #endif
81 }
82 
83 Agraph_t *readstring(char *string)
84 {
85  if (!gvc)
86  gv_init();
87  return agmemread(string);
88 }
89 
90 Agraph_t *read(FILE *f)
91 {
92  if (!gvc)
93  gv_init();
94 #ifdef WITH_CGRAPH
95  return agread(f, NULL);
96 #else
97  return agread(f);
98 #endif
99 }
100 
101 Agraph_t *read(const char *filename)
102 {
103  FILE *f;
104  Agraph_t *g;
105 
106  f = fopen(filename, "r");
107  if (!f)
108  return NULL;
109  if (!gvc)
110  gv_init();
111 #ifdef WITH_CGRAPH
112  g = agread(f, NULL);
113 #else
114  g = agread(f);
115 #endif
116  fclose(f);
117  return g;
118 }
119 
120 //-------------------------------------------------
121 Agraph_t *graph(Agraph_t *g, char *name)
122 {
123  if (!gvc)
124  gv_init();
125 #ifdef WITH_CGRAPH
126  return agsubg(g, name, 1);
127 #else
128  return agsubg(g, name);
129 #endif
130 }
131 
132 Agnode_t *node(Agraph_t *g, char *name)
133 {
134  if (!gvc)
135  return NULL;
136 #ifdef WITH_CGRAPH
137  return agnode(g, name, 1);
138 #else
139  // creating a protonode is not permitted
140  if (name[0] == '\001' && strcmp (name, "\001proto") == 0)
141  return NULL;
142  return agnode(g, name);
143 #endif
144 }
145 
147 {
148  if (!gvc || !t || !h)
149  return NULL;
150 #ifdef WITH_CGRAPH
151  // edges from/to the protonode are not permitted
152  if (AGTYPE(t) == AGRAPH || AGTYPE(h) == AGRAPH)
153  return NULL;
154  return agedge(agraphof(t), t, h, NULL, 1);
155 #else
156  // edges from/to the protonode are not permitted
157  if ((agnameof(t)[0] == '\001' && strcmp (agnameof(t), "\001proto") == 0)
158  || (agnameof(h)[0] == '\001' && strcmp (agnameof(h), "\001proto") == 0))
159  return NULL;
160  return agedge(t->graph, t, h);
161 #endif
162 }
163 
164 // induce tail if necessary
165 Agedge_t *edge(char *tname, Agnode_t *h)
166 {
167 #ifdef WITH_CGRAPH
168  return edge(node(agraphof(h), tname), h);
169 #else
170  return edge(node(h->graph, tname), h);
171 #endif
172 }
173 
174 // induce head if necessary
175 Agedge_t *edge(Agnode_t *t, char *hname)
176 {
177 #ifdef WITH_CGRAPH
178  return edge(t, node(agraphof(t), hname));
179 #else
180  return edge(t, node(t->graph, hname));
181 #endif
182 }
183 
184 // induce tail/head if necessary
185 Agedge_t *edge(Agraph_t *g, char *tname, char *hname)
186 {
187  return edge(node(g, tname), node(g, hname));
188 }
189 
190 //-------------------------------------------------
191 static char* myagxget(void *obj, Agsym_t *a)
192 {
193  int len;
194  char *val, *hs;
195 
196  if (!obj || !a)
197  return emptystring;
198 #ifndef WITH_CGRAPH
199  val = agxget(obj, a->index);
200 #else
201  val = agxget(obj, a);
202 #endif
203  if (!val)
204  return emptystring;
205  if (a->name[0] == 'l' && strcmp(a->name, "label") == 0 && aghtmlstr(val)) {
206  len = strlen(val);
207  hs = (char*)malloc(len + 3);
208  hs[0] = '<';
209  strcpy(hs+1, val);
210  hs[len+1] = '>';
211  hs[len+2] = '\0';
212  return hs;
213  }
214  return val;
215 }
216 char *getv(Agraph_t *g, Agsym_t *a)
217 {
218  return myagxget(g, a);
219 }
220 char *getv(Agraph_t *g, char *attr)
221 {
222  Agsym_t *a;
223 
224  if (!g || !attr)
225  return NULL;
226  a = agfindattr(agroot(g), attr);
227  return myagxget(g, a);
228 }
229 static void myagxset(void *obj, Agsym_t *a, char *val)
230 {
231  int len;
232  char *hs;
233 
234  if (a->name[0] == 'l' && val[0] == '<' && strcmp(a->name, "label") == 0) {
235  len = strlen(val);
236  if (val[len-1] == '>') {
237  hs = strdup(val+1);
238  *(hs+len-2) = '\0';
239 #ifdef WITH_CGRAPH
240  val = agstrdup_html(agraphof(obj),hs);
241 #else
242  val = agstrdup_html(hs);
243 #endif
244  free(hs);
245  }
246  }
247 #ifndef WITH_CGRAPH
248  agxset(obj, a->index, val);
249 #else
250  agxset(obj, a, val);
251 #endif
252 }
253 char *setv(Agraph_t *g, Agsym_t *a, char *val)
254 {
255  if (!g || !a || !val)
256  return NULL;
257  myagxset(g, a, val);
258  return val;
259 }
260 char *setv(Agraph_t *g, char *attr, char *val)
261 {
262  Agsym_t *a;
263 
264  if (!g || !attr || !val)
265  return NULL;
266  a = agfindattr(agroot(g), attr);
267  if (!a)
268  a = agraphattr(g->root, attr, emptystring);
269  myagxset(g, a, val);
270  return val;
271 }
272 //-------------------------------------------------
273 char *getv(Agnode_t *n, Agsym_t *a)
274 {
275  if (!n || !a)
276  return NULL;
277 #ifdef WITH_CGRAPH
278  if (AGTYPE(n) == AGRAPH) // protonode
279  return NULL; // FIXME ??
280 #endif
281  return myagxget(n, a);
282 }
283 char *getv(Agnode_t *n, char *attr)
284 {
285  Agraph_t *g;
286  Agsym_t *a;
287 
288  if (!n || !attr)
289  return NULL;
290 #ifdef WITH_CGRAPH
291  if (AGTYPE(n) == AGRAPH) // protonode
292  return NULL; // FIXME ??
293 #endif
294  g = agroot(agraphof(n));
295 #ifdef WITH_CGRAPH
296  a = agattr(g, AGNODE, attr, NULL);
297 #else
298  a = agfindattr(g->proto->n, attr);
299 #endif
300  return myagxget(n, a);
301 }
302 char *setv(Agnode_t *n, Agsym_t *a, char *val)
303 {
304  if (!n || !a || !val)
305  return NULL;
306 #ifdef WITH_CGRAPH
307  if (AGTYPE(n) == AGRAPH) // protonode
308  return NULL; // FIXME ??
309 #endif
310  myagxset(n, a, val);
311  return val;
312 }
313 char *setv(Agnode_t *n, char *attr, char *val)
314 {
315  Agraph_t *g;
316  Agsym_t *a;
317 
318  if (!n || !attr || !val)
319  return NULL;
320 #ifdef WITH_CGRAPH
321  if (AGTYPE(n) == AGRAPH) { // protonode
322  g = (Agraph_t*)n;
323  a = agattr(g, AGNODE, attr, val); // create default attribute in psuodo protonode
324  // FIXME? - deal with html in "label" attributes
325  return val;
326  }
327 #endif
328  g = agroot(agraphof(n));
329 #ifdef WITH_CGRAPH
330  a = agattr(g, AGNODE, attr, NULL);
331 #else
332  a = agfindattr(g->proto->n, attr);
333 #endif
334  if (!a)
335  a = agnodeattr(g, attr, emptystring);
336  myagxset(n, a, val);
337  return val;
338 }
339 //-------------------------------------------------
340 char *getv(Agedge_t *e, Agsym_t *a)
341 {
342  if (!e || !a)
343  return NULL;
344 #ifdef WITH_CGRAPH
345  if (AGTYPE(e) == AGRAPH) // protoedge
346  return NULL; // FIXME ??
347 #endif
348  return myagxget(e, a);
349 }
350 char *getv(Agedge_t *e, char *attr)
351 {
352  Agraph_t *g;
353  Agsym_t *a;
354 
355  if (!e || !attr)
356  return NULL;
357 #ifdef WITH_CGRAPH
358  if (AGTYPE(e) == AGRAPH) // protoedge
359  return NULL; // FIXME ??
360 #endif
361  g = agraphof(agtail(e));
362 #ifndef WITH_CGRAPH
363  a = agfindattr(g->proto->e, attr);
364 #else
365  a = agattr(g, AGEDGE, attr, NULL);
366 #endif
367  return myagxget(e, a);
368 }
369 char *setv(Agedge_t *e, Agsym_t *a, char *val)
370 {
371  if (!e || !a || !val)
372  return NULL;
373 #ifdef WITH_CGRAPH
374  if (AGTYPE(e) == AGRAPH) // protoedge
375  return NULL; // FIXME ??
376 #endif
377  myagxset(e, a, val);
378  return val;
379 }
380 char *setv(Agedge_t *e, char *attr, char *val)
381 {
382  Agraph_t *g;
383  Agsym_t *a;
384 
385  if (!e || !attr || !val)
386  return NULL;
387 #ifdef WITH_CGRAPH
388  if (AGTYPE(e) == AGRAPH) { // protoedge
389  g = (Agraph_t*)e;
390  a = agattr(g, AGEDGE, attr, val); // create default attribute in pseudo protoedge
391  // FIXME? - deal with html in "label" attributes
392  return val;
393  }
394 #endif
395  g = agroot(agraphof(agtail(e)));
396 #ifndef WITH_CGRAPH
397  a = agfindattr(g->proto->e, attr);
398  if (!a)
399  a = agedgeattr(g, attr, emptystring);
400 #else
401  a = agattr(g, AGEDGE, attr, NULL);
402  if (!a)
403  a = agattr(g, AGEDGE, attr, emptystring);
404 #endif
405  myagxset(e, a, val);
406  return val;
407 }
408 //-------------------------------------------------
409 Agraph_t *findsubg(Agraph_t *g, char *name)
410 {
411  if (!g || !name)
412  return NULL;
413 #ifndef WITH_CGRAPH
414  return agfindsubg(g, name);
415 #else
416  return agsubg(g, name, 0);
417 #endif
418 }
419 
420 Agnode_t *findnode(Agraph_t *g, char *name)
421 {
422  if (!g || !name)
423  return NULL;
424 #ifndef WITH_CGRAPH
425  return agfindnode(g, name);
426 #else
427  return agnode(g, name, 0);
428 #endif
429 }
430 
432 {
433  if (!t || !h)
434  return NULL;
435 #ifdef WITH_CGRAPH
436  if (AGTYPE(t) == AGRAPH || AGTYPE(h) == AGRAPH)
437  return NULL;
438 #endif
439  return agfindedge(agraphof(t), t, h);
440 }
441 
442 Agsym_t *findattr(Agraph_t *g, char *name)
443 {
444  if (!g || !name)
445  return NULL;
446  return agfindattr(g, name);
447 }
448 
449 Agsym_t *findattr(Agnode_t *n, char *name)
450 {
451  if (!n || !name)
452  return NULL;
453  return agfindattr(n, name);
454 }
455 
456 Agsym_t *findattr(Agedge_t *e, char *name)
457 {
458  if (!e || !name)
459  return NULL;
460  return agfindattr(e, name);
461 }
462 
463 //-------------------------------------------------
464 
466 {
467  if (!e)
468  return NULL;
469 #ifdef WITH_CGRAPH
470  if (AGTYPE(e) == AGRAPH)
471  return NULL;
472 #endif
473  return aghead(e);
474 }
475 
477 {
478  if (!e)
479  return NULL;
480 #ifdef WITH_CGRAPH
481  if (AGTYPE(e) == AGRAPH)
482  return NULL;
483 #endif
484  return agtail(e);
485 }
486 
488 {
489  if (!g || g == g->root)
490  return NULL;
491  return agroot(g);
492 }
493 
495 {
496  if (!e)
497  return NULL;
498 #ifdef WITH_CGRAPH
499  if (AGTYPE(e) == AGRAPH)
500  return (Agraph_t*)e; /* graph of protoedge is itself recast */
501 #endif
502  return agraphof(agtail(e));
503 }
504 
506 {
507  if (!n)
508  return NULL;
509 #ifdef WITH_CGRAPH
510  if (AGTYPE(n) == AGRAPH)
511  return (Agraph_t*)n; /* graph of protonode is itself recast */
512 #endif
513  return agraphof(n);
514 }
515 
517 {
518  if (!g)
519  return NULL;
520  return agroot(g);
521 }
522 
523 //-------------------------------------------------
525 {
526  if (!g)
527  return NULL;
528 #ifdef WITH_CGRAPH
529  return (Agnode_t *)g; // gross abuse of the type system!
530 #else
531  return g->proto->n;
532 #endif
533 }
534 
536 {
537  if (!g)
538  return NULL;
539 #ifdef WITH_CGRAPH
540  return (Agedge_t *)g; // gross abuse of the type system!
541 #else
542  return g->proto->e;
543 #endif
544 }
545 
546 //-------------------------------------------------
547 char *nameof(Agraph_t *g)
548 {
549  if (!g)
550  return NULL;
551  return agnameof(g);
552 }
553 char *nameof(Agnode_t *n)
554 {
555  if (!n)
556  return NULL;
557 #ifdef WITH_CGRAPH
558  if (AGTYPE(n) == AGRAPH)
559  return NULL;
560 #endif
561  return agnameof(n);
562 }
563 //char *nameof(Agedge_t *e)
564 //{
565 // if (!e)
566 // return NULL;
567 //#ifdef WITH_CGRAPH
568 // if (AGTYPE(e) == AGRAPH)
569 // return NULL;
570 //#endif
571 // return agnameof(e);
572 //}
573 char *nameof(Agsym_t *a)
574 {
575  if (!a)
576  return NULL;
577  return a->name;
578 }
579 
580 //-------------------------------------------------
581 bool ok(Agraph_t *g)
582 {
583  if (!g)
584  return false;
585  return true;
586 }
587 bool ok(Agnode_t *n)
588 {
589  if (!n)
590  return false;
591  return true;
592 }
593 bool ok(Agedge_t *e)
594 {
595  if (!e)
596  return false;
597  return true;
598 }
599 bool ok(Agsym_t *a)
600 {
601  if (!a)
602  return false;
603  return true;
604 }
605 //-------------------------------------------------
607 {
608 #ifndef WITH_CGRAPH
609  Agraph_t *mg;
610  Agnode_t *n;
611  Agedge_t *e;
612 #endif
613 
614  if (!g)
615  return NULL;
616 #ifdef WITH_CGRAPH
617  return agfstsubg(g);
618 #else
619  n = g->meta_node;
620  if (!n)
621  return NULL;
622  mg = agraphof(n);
623  if (!mg)
624  return NULL;
625  e = agfstout(mg, n);
626  if (!e)
627  return NULL;
628  return agusergraph(aghead(e));
629 #endif
630 }
631 
633 {
634 #ifndef WITH_CGRAPH
635  Agraph_t *mg;
636  Agnode_t *ng, *nsg;
637  Agedge_t *e;
638 #endif
639 
640  if (!g || !sg)
641  return NULL;
642 #ifdef WITH_CGRAPH
643  return agnxtsubg(sg);
644 #else
645  ng = g->meta_node;
646  nsg = sg->meta_node;
647  if (!ng || !nsg)
648  return NULL;
649  mg = agraphof(ng);
650  if (!mg)
651  return NULL;
652  e = agfindedge(mg, ng, nsg);
653  if (!e)
654  return NULL;
655  e = agnxtout(mg, e);
656  if (!e)
657  return NULL;
658  return agusergraph(aghead(e));
659 #endif
660 }
661 
662 #ifdef WITH_CGRAPH
664 {
665  return g->parent;
666 }
667 
669 {
670  return NULL;
671 }
672 #else
674 {
675  Agraph_t *mg;
676  Agnode_t *n;
677  Agedge_t *e;
678 
679  if (!g)
680  return NULL;
681  n = g->meta_node;
682  if (!n)
683  return NULL;
684  mg = agraphof(n);
685  if (!mg)
686  return NULL;
687  e = agfstin(mg, n);
688  if (!e)
689  return NULL;
690  return agusergraph(agtail(e));
691 }
692 
694 {
695  Agraph_t *mg;
696  Agnode_t *ng, *nsg;
697  Agedge_t *e;
698 
699  if (!g || !sg)
700  return NULL;
701  ng = g->meta_node;
702  nsg = sg->meta_node;
703  if (!ng || !nsg)
704  return NULL;
705  mg = agraphof(ng);
706  if (!mg)
707  return NULL;
708  e = agfindedge(mg, nsg, ng);
709  if (!e)
710  return NULL;
711  e = agnxtin(mg, e);
712  if (!e)
713  return NULL;
714  return agusergraph(agtail(e));
715 }
716 #endif
717 
719 {
720  Agnode_t *n;
721  Agedge_t *e;
722 
723  if (!g)
724  return NULL;
725  for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
726  e = agfstout(g, n);
727  if (e) return e;
728  }
729  return NULL;
730 }
731 
733 {
734  Agnode_t *n;
735  Agedge_t *ne;
736 
737  if (!g || !e)
738  return NULL;
739  ne = agnxtout(g, e);
740  if (ne)
741  return (ne);
742  for (n = agnxtnode(g, agtail(e)); n; n = agnxtnode(g, n)) {
743  ne = agfstout(g, n);
744  if (ne) return ne;
745  }
746  return NULL;
747 }
748 
750 {
751  return firstout(g);
752 }
753 
755 {
756  return nextout(g, e);
757 }
758 
760 {
761  if (!n)
762  return NULL;
763  return agfstout(agraphof(n), n);
764 }
765 
767 {
768  if (!n || !e)
769  return NULL;
770  return agnxtout(agraphof(n), e);
771 }
772 
774 {
775  Agedge_t *e;
776 
777  if (!n)
778  return NULL;
779  e = agfstout(agraphof(n), n);
780  if (!e)
781  return NULL;
782  return aghead(e);
783 }
784 
786 {
787  Agedge_t *e;
788  Agraph_t *g;
789 
790  if (!n || !h)
791  return NULL;
792  g = agraphof(n);
793  e = agfindedge(g, n, h);
794  if (!e)
795  return NULL;
796  do {
797  e = agnxtout(g, e);
798  if (!e)
799  return NULL;
800  } while (aghead(e) == h);
801  return aghead(e);
802 }
803 
805 {
806  if (!n)
807  return NULL;
808  return agfstedge(agraphof(n), n);
809 }
810 
812 {
813  if (!n || !e)
814  return NULL;
815  return agnxtedge(agraphof(n), e, n);
816 }
817 
819 {
820  Agnode_t *n;
821 
822  if (!g)
823  return NULL;
824  n = agfstnode(g);
825  if (!n)
826  return NULL;
827  return agfstin(g, n);
828 }
829 
831 {
832  Agnode_t *n;
833  Agedge_t *ne;
834 
835  if (!g || !e)
836  return NULL;
837  ne = agnxtin(g, e);
838  if (ne)
839  return (ne);
840  n = agnxtnode(g, aghead(e));
841  if (!n)
842  return NULL;
843  return agfstin(g, n);
844 }
845 
847 {
848  if (!n)
849  return NULL;
850  return agfstin(agraphof(n), n);
851 }
852 
854 {
855  if (!n || !e)
856  return NULL;
857  return agnxtin(agraphof(n), e);
858 }
859 
861 {
862  Agedge_t *e;
863 
864  if (!n)
865  return NULL;
866  e = agfstin(agraphof(n), n);
867  if (!e)
868  return NULL;
869  return agtail(e);
870 }
871 
873 {
874  Agedge_t *e;
875  Agraph_t *g;
876 
877  if (!n || !t)
878  return NULL;
879  g = agraphof(n);
880  e = agfindedge(g, t, n);
881  if (!e)
882  return NULL;
883  do {
884  e = agnxtin(g, e);
885  if (!e)
886  return NULL;
887  } while (agtail(e) == t);
888  return agtail(e);
889 }
890 
892 {
893  if (!g)
894  return NULL;
895  return agfstnode(g);
896 }
897 
899 {
900  if (!g || !n)
901  return NULL;
902  return agnxtnode(g, n);
903 }
904 
906 {
907  if (!e)
908  return NULL;
909  return agtail(e);
910 }
911 
913 {
914  if (!e || n != agtail(e))
915  return NULL;
916  return aghead(e);
917 }
918 
920 {
921  if (!g)
922  return NULL;
923  g = agroot(g);
924 #ifdef WITH_CGRAPH
925  return agnxtattr(g,AGRAPH,NULL);
926 #else
927  if (dtsize(g->univ->globattr->dict) == 0)
928  return NULL;
929  return g->univ->globattr->list[0];
930 #endif
931 }
932 
934 {
935  int i;
936 
937  if (!g || !a)
938  return NULL;
939  g = agroot(g);
940 #ifdef WITH_CGRAPH
941  return agnxtattr(g,AGRAPH,a);
942 #else
943  for (i = 0; i < dtsize(g->univ->globattr->dict); i++)
944  if (a == g->univ->globattr->list[i])
945  break;
946  i++;
947  if (i > dtsize(g->univ->globattr->dict))
948  return NULL;
949  return g->univ->globattr->list[i];
950 #endif
951 }
952 
954 {
955  Agraph_t *g;
956 
957  if (!n)
958  return NULL;
959  g = agraphof(n);
960 #ifdef WITH_CGRAPH
961  return agnxtattr(g,AGNODE,NULL);
962 #else
963  if (dtsize(g->univ->nodeattr->dict) == 0)
964  return NULL;
965  return g->univ->nodeattr->list[0];
966 #endif
967 }
968 
970 {
971  Agraph_t *g;
972  int i;
973 
974  if (!n || !a)
975  return NULL;
976  g = agraphof(n);
977 #ifdef WITH_CGRAPH
978  return agnxtattr(g,AGNODE,a);
979 #else
980  for (i = 0; i < dtsize(g->univ->nodeattr->dict); i++)
981  if (a == g->univ->nodeattr->list[i])
982  break;
983  i++;
984  if (i > dtsize(g->univ->nodeattr->dict))
985  return NULL;
986  return g->univ->nodeattr->list[i];
987 #endif
988 }
989 
991 {
992  Agraph_t *g;
993 
994  if (!e)
995  return NULL;
996  g = agraphof(agtail(e));
997 #ifdef WITH_CGRAPH
998  return agnxtattr(g,AGEDGE,NULL);
999 #else
1000  if (dtsize(g->univ->edgeattr->dict) == 0)
1001  return NULL;
1002  return g->univ->edgeattr->list[0];
1003 #endif
1004 }
1005 
1007 {
1008  Agraph_t *g;
1009  int i;
1010 
1011  if (!e || !a)
1012  return NULL;
1013  g = agraphof(agtail(e));
1014 #ifdef WITH_CGRAPH
1015  return agnxtattr(g,AGEDGE,a);
1016 #else
1017  for (i = 0; i < dtsize(g->univ->edgeattr->dict); i++)
1018  if (a == g->univ->edgeattr->list[i])
1019  break;
1020  i++;
1021  if (i > dtsize(g->univ->edgeattr->dict))
1022  return NULL;
1023  return g->univ->edgeattr->list[i];
1024 #endif
1025 }
1026 
1027 bool rm(Agraph_t *g)
1028 {
1029  Agedge_t *e;
1030 
1031  if (!g)
1032  return false;
1033 #ifdef WITH_CGRAPH
1034  Agraph_t* sg;
1035  for (sg = agfstsubg (g); sg; sg = agnxtsubg (sg))
1036  rm(sg);
1037  if (g == agroot(g))
1038  agclose(g);
1039  else
1040  agdelete(agroot(g), g);
1041  return true;
1042 #else
1043  if (g->meta_node) {
1044  for (e = agfstout(g->meta_node->graph, g->meta_node); e;
1045  e = agnxtout(g->meta_node->graph, e)) {
1046  rm(agusergraph(aghead(e)));
1047  }
1048  if (g == agroot(g)) {
1049  agclose(g);
1050  } else {
1051  agdelete(g->meta_node->graph, g);
1052  }
1053  return true;
1054  }
1055  fprintf(stderr, "subgraph has no meta_node\n");
1056  return false;
1057 #endif
1058 }
1059 
1060 bool rm(Agnode_t *n)
1061 {
1062  if (!n)
1063  return false;
1064  // removal of the protonode is not permitted
1065  if (agnameof(n)[0] == '\001' && strcmp (agnameof(n), "\001proto") ==0)
1066  return false;
1067  agdelete(agraphof(n), n);
1068  return true;
1069 }
1070 
1071 bool rm(Agedge_t *e)
1072 {
1073  if (!e)
1074  return false;
1075  // removal of the protoedge is not permitted
1076  if ((agnameof(aghead(e))[0] == '\001' && strcmp (agnameof(aghead(e)), "\001proto") == 0)
1077  || (agnameof(agtail(e))[0] == '\001' && strcmp (agnameof(agtail(e)), "\001proto") == 0))
1078  return false;
1079  agdelete(agroot(agraphof(aghead(e))), e);
1080  return true;
1081 }
1082 
1083 bool layout(Agraph_t *g, const char *engine)
1084 {
1085  int err;
1086 
1087  if (!g)
1088  return false;
1089  err = gvFreeLayout(gvc, g); /* ignore errors */
1090  err = gvLayout(gvc, g, engine);
1091  return (! err);
1092 }
1093 
1094 // annotate the graph with layout information
1096 {
1097  if (!g)
1098  return false;
1099  attach_attrs(g);
1100  return true;
1101 }
1102 
1103 // render to stdout
1104 bool render(Agraph_t *g, const char *format)
1105 {
1106  int err;
1107 
1108  if (!g)
1109  return false;
1110  err = gvRender(gvc, g, format, stdout);
1111  return (! err);
1112 }
1113 
1114 // render to an open FILE
1115 bool render(Agraph_t *g, const char *format, FILE *f)
1116 {
1117  int err;
1118 
1119  if (!g)
1120  return false;
1121  err = gvRender(gvc, g, format, f);
1122  return (! err);
1123 }
1124 
1125 // render to an open channel
1126 bool renderchannel(Agraph_t *g, const char *format, const char *channelname)
1127 {
1128  int err;
1129 
1130  if (!g)
1131  return false;
1133  err = gvRender(gvc, g, format, (FILE*)channelname);
1134  return (! err);
1135 }
1136 
1137 // render to a filename
1138 bool render(Agraph_t *g, const char *format, const char *filename)
1139 {
1140  int err;
1141 
1142  if (!g)
1143  return false;
1144  err = gvRenderFilename(gvc, g, format, filename);
1145  return (! err);
1146 }
1147 
1148 // render to string result, using binding-dependent gv_string_writer()
1149 void renderresult(Agraph_t *g, const char *format, char *outdata)
1150 {
1151  int err;
1152 
1153  if (!g)
1154  return;
1155  gv_string_writer_init(gvc);
1156  err = gvRender(gvc, g, format, (FILE*)outdata);
1157 }
1158 
1159 // render to a malloc'ed data string, to be free'd by caller.
1160 char* renderdata(Agraph_t *g, const char *format)
1161 {
1162  int err;
1163  char *data;
1164  unsigned int length;
1165 
1166  if (!g)
1167  return NULL;
1168  err = gvRenderData(gvc, g, format, &data, &length);
1169  if (err)
1170  return NULL;
1171  data = (char*)realloc(data, length + 1);
1172  return data;
1173 }
1174 
1175 bool write(Agraph_t *g, FILE *f)
1176 {
1177  int err;
1178 
1179  if (!g)
1180  return false;
1181  err = agwrite(g, f);
1182  return (! err);
1183 }
1184 
1185 bool write(Agraph_t *g, const char *filename)
1186 {
1187  FILE *f;
1188  int err;
1189 
1190  if (!g)
1191  return false;
1192  f = fopen(filename, "w");
1193  if (!f)
1194  return false;
1195  err = agwrite(g, f);
1196  fclose(f);
1197  return (! err);
1198 }