1 /*=======================rap==================================================*\
3 * Graph support for Lua.
5 * 30-7-2006, 30-12-2016, 01/2017
7 * Graph related functionality.
9 \*=========================================================================*/
11 /*=========================================================================*\
13 \*=========================================================================*/
21 #include "graphviz/cdt.h"
22 #include "graphviz/gvplugin.h"
24 /*=========================================================================*\
26 \*=========================================================================*/
27 #define MYNAME "graph"
28 #define MYVERSION "2.0.0"
29 #if defined(_WIN32) || defined(WIN32)
30 #define MYGVVERSION "2.38"
31 #define MYSYSTEM "Win32"
33 #define MYSYSTEM "other"
34 #define MYGVVERSION GVVERSION
36 #define MYCOPYRIGHT "Copyright (C) 2006-2017, Herbert Leuwer "
37 #define MYDESCRIPTION "LuaGRAPH is a library for creating, manipulating and rendering GRAPHs (based on the GRAPHVIZ library cgraph)."
39 /*=========================================================================*\
41 \*=========================================================================*/
42 static int gr_open(lua_State
*L
);
43 static int gr_close(lua_State
*L
);
44 static int gr_nameof(lua_State
*L
);
45 static int gr_write(lua_State
*L
);
46 static int gr_read(lua_State
*L
);
47 static int gr_memread(lua_State
*L
);
48 static int gr_isstrict(lua_State
*L
);
49 static int gr_isdirected(lua_State
*L
);
50 static int gr_nnodes(lua_State
*L
);
51 static int gr_nedges(lua_State
*L
);
52 static int gr_subgraph(lua_State
*L
);
53 static int gr_root(lua_State
*L
);
54 static int gr_parent(lua_State
*L
);
55 static int gr_equal(lua_State
*L
);
56 static int gr_getnext(lua_State
*L
);
57 static int gr_walk(lua_State
*L
);
58 static int gr_id(lua_State
*L
);
59 static int gr_isroot(lua_State
*L
);
60 static int gr_getgraphattr(lua_State
*L
);
61 static int gr_getnodeattr(lua_State
*L
);
62 static int gr_getedgeattr(lua_State
*L
);
63 static int gr_getattr(lua_State
*L
);
64 static int gr_setgraphattr(lua_State
*L
);
65 static int gr_setnodeattr(lua_State
*L
);
66 static int gr_setedgeattr(lua_State
*L
);
67 static int gr_setattr(lua_State
*L
);
68 static int gr_delete(lua_State
*L
);
69 static int gr_node(lua_State
*L
);
70 static int gr_edge(lua_State
*L
);
71 static int gr_findedge(lua_State
*L
);
72 static int gr_idnode(lua_State
*L
);
73 static int gr_nextnode(lua_State
*L
);
74 static int gr_walknodes(lua_State
*L
);
75 static int gr_graphof(lua_State
*L
);
76 static int gr_contains(lua_State
*L
);
77 static int gr_layout(lua_State
*L
);
78 static int gr_freelayout(lua_State
*L
);
79 static int gr_render(lua_State
*L
);
80 static int gr_plugins(lua_State
*L
);
81 static int gr_tostring(lua_State
*L
);
83 /*=========================================================================*\
85 \*=========================================================================*/
88 #ifdef USE_BUILTIN_PLUGINS
89 extern gvplugin_library_t gvplugin_dot_layout_LTX_library
;
90 extern gvplugin_library_t gvplugin_neato_layout_LTX_library
;
91 extern gvplugin_library_t gvplugin_gd_LTX_library
;
92 extern gvplugin_library_t gvplugin_pango_LTX_library
;
94 extern gvplugin_library_t gvplugin_webp_LTX_library
;
96 extern gvplugin_library_t gvplugin_core_LTX_library
;
98 lt_symlist_t lt_preloaded_symbols
[] = {
99 { "gvplugin_dot_layout_LTX_library", (void*)(&gvplugin_dot_layout_LTX_library
) },
100 { "gvplugin_neato_layout_LTX_library", (void*)(&gvplugin_neato_layout_LTX_library
) },
101 { "gvplugin_pango_LTX_library", (void*)(&gvplugin_pango_LTX_library
) },
103 { "gvplugin_webp_LTX_library", (void*)(&gvplugin_webp_LTX_library
) },
105 { "gvplugin_gd_LTX_library", (void*)(&gvplugin_gd_LTX_library
) },
106 { "gvplugin_core_LTX_library", (void*)(&gvplugin_core_LTX_library
) },
110 lt_symlist_t lt_preloaded_symbols
[] = {
115 * Base library functions
117 static const luaL_Reg funcs
[] = {
121 {"memread", gr_memread
},
127 * Graph object methods
129 static const luaL_Reg reg_methods
[] = {
132 {"subgraph", gr_subgraph
},
133 {"getnext", gr_getnext
},
134 {"nextgraph", gr_getnext
},
136 {"walkgraphs", gr_walk
},
137 {"getgraphattr", gr_getgraphattr
},
138 {"getnodeattr", gr_getnodeattr
},
139 {"getedgeattr", gr_getedgeattr
},
140 {"getattr", gr_getattr
},
141 {"defaults", gr_getattr
},
142 {"setgraphattr", gr_setgraphattr
},
143 {"setnodeattr", gr_setnodeattr
},
144 {"setedgeattr", gr_setedgeattr
},
145 {"setattr", gr_setattr
},
146 {"declare", gr_setattr
},
147 {"delete", gr_delete
},
150 {"findedge", gr_findedge
},
151 {"idnode", gr_idnode
},
152 {"nextnode", gr_nextnode
},
153 {"walknodes", gr_walknodes
},
154 {"type", get_object_type
},
155 {"contains", gr_contains
},
156 {"layout", gr_layout
},
157 {"freelayout", gr_freelayout
},
158 {"render", gr_render
},
166 static const luaL_Reg reg_rmembers
[] = {
167 {"nnodes", gr_nnodes
},
168 {"nedges", gr_nedges
},
170 {"status", gr_status
},
172 {"parent", gr_parent
},
173 {"isstrict", gr_isstrict
},
174 {"isdirected", gr_isdirected
},
175 {"isroot", gr_isroot
},
176 {"graph", gr_graphof
},
182 * Standard metamethods
184 static const luaL_Reg reg_metamethods
[] = {
185 {"__gc", gr_collect
},
187 {"__newindex", object_newindex_handler
},
188 {"__tostring", gr_tostring
},
193 * Event callback functions - used for registring/unregstring proxy objects
196 static const struct Agcbdisc_s disc
= {
197 { cb_insert
, cb_modify
, cb_delete
}, /* graph callbacks */
198 { cb_insert
, cb_modify
, cb_delete
}, /* node callbacks */
199 { cb_insert
, cb_modify
, cb_delete
}, /* edge callbacks */
202 /*=========================================================================*\
204 \*=========================================================================*/
205 /*-------------------------------------------------------------------------*\
207 \*-------------------------------------------------------------------------*/
210 * Create a new Lua userdata object and configure metatable.
212 static int new_graph(lua_State
*L
){
213 return new_object(L
, "graph", reg_rmembers
, reg_methods
,
214 reg_metamethods
, object_index_handler
);
217 #define DEMAND_LOADING (1)
220 * Layout a graph using given engine.
222 static int gv_layout(Agraph_t
*g
, const char *engine
)
224 int rv
= gvLayout(gvc
, g
, engine
);
230 static int gv_free_layout(Agraph_t
*g
)
232 int rv
= gvFreeLayout(gvc
, g
);
239 * Render layouted graph into a file given by file handle
240 * using given format.
242 static int gv_render(Agraph_t
*g
, const char *fmt
, FILE *fout
)
244 int rv
= gvRender(gvc
, g
, fmt
, fout
);
250 * Render layouted graph into a file given by file name using given
253 static int gv_render_file(Agraph_t
*g
, const char *fmt
, const char *fname
)
255 int rv
= gvRenderFilename(gvc
, g
, fmt
, fname
);
262 * Gets attributes for a specific object type into a table rt.
263 * If no attributes are defined, the function returns an empty table.
264 * Lua entry stack: ud
265 * Lua exit stack: ud, rt
267 static int getattr(lua_State
*L
, int kind
)
269 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
272 lua_newtable(L
); /* rt */
277 while ((sym
= agnxtattr(ud
->g
, kind
, sym
)) != NULL
){
278 lua_pushstring(L
, sym
->name
); /* rt, key */
279 lua_pushstring(L
, sym
->defval
); /* rt, key, value */
280 lua_settable(L
, -3); /* rt */
285 return luaL_error(L
, "invalid kind");
291 * Sets attributes for a specific object type from a table.
292 * Lua entry stack: ud, t
293 * Lua exit stack: ud, t
295 static int setattr(lua_State
*L
, int kind
)
300 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
302 /* Parameter check */
303 luaL_checktype(L
, -1, LUA_TTABLE
);
305 /* traverse through all key/value pairs of given table */
306 lua_pushnil(L
); /* t, nil */
307 while (lua_next(L
, -2)){ /* t, key, val */
308 if (!lua_isstring(L
, -2)){
309 lua_pop(L
, 2); /* t */
310 luaL_error(L
, "invalid key");
313 key
= (char *) lua_tostring(L
, -2);
314 value
= (char *) lua_tostring(L
, -1);
317 luaL_error(L
, "invalid value");
320 lua_pop(L
, 1); /* t, key */
322 agattr(ud
->g
, kind
, key
, value
);
324 lua_pushnumber(L
, n
);
328 /*-------------------------------------------------------------------------* \
329 * Function: g, err = graph.open(name [,kind])
331 * Returns graph userdata.
333 * g, err = graph.open(name[, kind])
334 \*-------------------------------------------------------------------------*/
335 static int gr_open(lua_State
*L
)
338 Agdesc_t kind
= Agdirected
;
339 char *name
= (char *) luaL_checkstring(L
, 1);
340 char *skind
= (char *) luaL_optstring(L
, 2, "directed");
342 if (!strcmp(skind
, "directed")) {
344 } else if (!strcmp(skind
, "strictdirected")){
345 kind
= Agstrictdirected
;
346 } else if (!strcmp(skind
, "undirected")) {
348 } else if (!strcmp(skind
, "strictundirected")) {
349 kind
= Agstrictundirected
;
351 luaL_error(L
, "invalid graph attribute");
354 /* Create a userdata */
355 ud
= lua_newuserdata(L
, sizeof(gr_graph_t
)); /* ud */
356 if (!(ud
->g
= agopen(name
, kind
, &AgDefaultDisc
))){
358 lua_pushstring(L
, "open failed");
361 ud
->name
= strdup(name
);
364 /* We need a few default fields */
365 if (!agattr(ud
->g
, AGEDGE
, "label", "") ||
366 !agattr(ud
->g
, AGRAPH
, "__attrib__", "") ||
367 !agattr(ud
->g
, AGEDGE
, "__attrib__", "") ||
368 !agattr(ud
->g
, AGNODE
, "__attrib__", "")){
369 luaL_error(L
, "declaration failed");
373 /* We set callbacks only in root graph */
374 if (ud
->g
== agroot(ud
->g
)){
375 agpushdisc(ud
->g
, (struct Agcbdisc_s
*)&disc
, L
);
378 set_object(L
, ud
->g
);
382 /*-------------------------------------------------------------------------*\
383 * Method: g.close(self)
387 * rv = graph.close(g)
389 \*-------------------------------------------------------------------------*/
390 static int gr_close(lua_State
*L
)
392 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
396 if (ud
->status
== ALIVE
) {
397 /* Delete the graph, if it still exists */
398 TRACE(" g:close(): graph: ud=%p '%s' ptr=%p type=%d (%s %d)\n",
399 (void *) ud
, agnameof(ud
->g
), (void *)ud
->g
, AGTYPE(ud
->g
), __FILE__
, __LINE__
);
403 TRACE(" g:close(): graph: ud=%p already closed (%s %d)\n", (void *) ud
, __FILE__
, __LINE__
);
405 lua_pushnumber(L
, 0);
410 /*-------------------------------------------------------------------------*\
411 * Property: name [string]
412 * Returns the name of a graph.
415 \*-------------------------------------------------------------------------*/
416 static int gr_nameof(lua_State
*L
)
418 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
419 if (ud
->status
!= ALIVE
){
420 luaL_error(L
, "deleted");
423 lua_pushstring(L
, agnameof(ud
->g
));
427 /*-------------------------------------------------------------------------*\
428 * Property: isstrict [boolean]
429 * Returns true is graph is strict, otherwise it returns false.
432 \*-------------------------------------------------------------------------*/
433 static int gr_isstrict(lua_State
*L
)
435 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
436 lua_pushboolean(L
, agisstrict(ud
->g
));
440 /*-------------------------------------------------------------------------*\
441 * Property: isdirected [boolean]
442 * Returns true if the graph is directed, otherwise it returns false
445 \*-------------------------------------------------------------------------*/
446 static int gr_isdirected(lua_State
*L
)
448 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
449 lua_pushboolean(L
, agisdirected(ud
->g
));
453 /*-------------------------------------------------------------------------* \
454 * Read a graph from a string
455 * Returns graph userdata.
457 * g, err = graph.memread(str)
458 \*-------------------------------------------------------------------------*/
459 static int gr_memread(lua_State
*L
)
462 char *str
= (char *)lua_tostring(L
, 1);
464 ud
= lua_newuserdata(L
, sizeof(gr_graph_t
));
465 if (!(ud
->g
= agmemread(str
))){
467 lua_pushstring(L
, "agread failed");
470 ud
->name
= strdup(agnameof(ud
->g
));
474 /* We set callbacks only in root graph */
475 if (ud
->g
== agroot(ud
->g
)){
476 agpushdisc(ud
->g
, (struct Agcbdisc_s
*)&disc
, L
);
478 set_object(L
, ud
->g
);
482 /*-------------------------------------------------------------------------* \
483 * Read a graph from a file; "stdin" reads from STDIN.
484 * Returns graph userdata.
486 * g, err = graph.read(filename)
487 \*-------------------------------------------------------------------------*/
488 static int gr_read(lua_State
*L
)
492 char *fname
= (char *)luaL_optstring(L
, 1, "stdin");
494 if (!strcmp(fname
, "stdin")){
497 fin
= fopen(fname
, "r");
501 lua_pushstring(L
, "fopen failed");
504 /* Create a userdata */
505 ud
= lua_newuserdata(L
, sizeof(gr_graph_t
));
506 if (!(ud
->g
= agread(fin
, NIL(Agdisc_t
*)))){
509 lua_pushstring(L
, "agread failed");
513 ud
->name
= strdup(agnameof(ud
->g
));
517 /* We set callbacks only in root graph */
518 if (ud
->g
== agroot(ud
->g
)){
519 agpushdisc(ud
->g
, (struct Agcbdisc_s
*)&disc
, L
);
521 set_object(L
, ud
->g
);
525 /*-------------------------------------------------------------------------*\
526 * Method: g.write(self, filename)
527 * Write a graph into a file.
528 * Returns 1 on success, nil plus error message on failure.
529 * rv, err = g:write(filename)
530 \*-------------------------------------------------------------------------*/
531 static int gr_write(lua_State
*L
)
536 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
537 char *fname
= (char*) luaL_optstring(L
, 2, "__std__");
539 if (!strcmp(fname
, "__std__")){
543 fout
= fopen(fname
, "w+");
547 lua_pushstring(L
, "fopen failed");
550 rv
= agwrite(ud
->g
, fout
);
555 lua_pushstring(L
, "agwrite failed");
560 lua_pushnumber(L
, rv
);
564 /*-------------------------------------------------------------------------*\
565 * Property:nnodes [number]
566 * Provides the numebr of edges of a graph
569 \*-------------------------------------------------------------------------*/
570 static int gr_nnodes(lua_State
*L
)
572 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
573 lua_pushnumber(L
, agnnodes(ud
->g
));
577 /*-------------------------------------------------------------------------*\
578 * Property: nedges [number]
579 * Provides the number of edges of a graph.
582 \*-------------------------------------------------------------------------*/
583 static int gr_nedges(lua_State
*L
)
585 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
586 lua_pushnumber(L
, agnedges(ud
->g
));
590 /*-------------------------------------------------------------------------*\
591 * Metamethod: __eq [boolean]
594 * g == h or g != h with metamethods
595 \*-------------------------------------------------------------------------*/
596 static int gr_equal(lua_State
*L
)
598 gr_graph_t
*ud1
= tograph(L
, 1, STRICT
);
599 gr_graph_t
*ud2
= tograph(L
, 2, STRICT
);
600 lua_pushboolean(L
, (ud1
->g
== ud2
->g
));
604 /*-------------------------------------------------------------------------*\
605 * Method: g.subgraph(self, name, nocreate)
606 * Find or create a subgraph. The optional flag nocreate=true inhibits creation.
607 * Returns graph userdata.
609 * sg, err = g:subgraph(name)
610 \*-------------------------------------------------------------------------*/
611 static int gr_subgraph(lua_State
*L
)
616 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
617 char *name
= (char *) luaL_checkstring(L
, 2);
619 /* Check wheter the subgraph already exists */
620 if ((g
= agsubg(ud
->g
, name
, 0)) != NULL
){
622 /* Yes - return corresponing userdata */
623 rv
= get_object(L
, g
);
624 if (lua_isnil(L
, -rv
)){
625 sg
= lua_newuserdata(L
, sizeof(gr_graph_t
));
627 sg
->name
= strdup(name
);
634 /* No - Create a new one */
635 if (lua_toboolean(L
, 3)){
637 lua_pushstring(L
, "graph not found");
640 sg
= lua_newuserdata(L
, sizeof(gr_graph_t
));
641 if (!(sg
->g
= agsubg(ud
->g
, name
, 1))){
643 lua_pushstring(L
, "agsubg failed");
647 sg
->name
= strdup(name
);
654 /*-------------------------------------------------------------------------*\
655 * Property: root [userdata]
659 \*-------------------------------------------------------------------------*/
660 static int gr_root(lua_State
*L
)
662 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
665 if (!(g
= agroot(ud
->g
))){
667 lua_pushstring(L
, "agroot failed");
669 lua_pop(L
, 1); /* empty */
670 return get_object(L
, g
); /* root */
673 /*-------------------------------------------------------------------------*\
674 * Property: parent [userdata]
675 * Get parent of graph.
678 \*-------------------------------------------------------------------------*/
679 static int gr_parent(lua_State
*L
)
681 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
682 Agraph_t
*g
= agparent(ud
->g
);
684 return get_object(L
, g
);
687 /*-------------------------------------------------------------------------*\
688 * Method: g = getnext(self, prev)
689 * Retrieves next subgraph in a graph hierarchy.
690 * Returns the successor of the given graph. With nil for parameter prev the
691 * first successor is returned.
693 * first = g:getnext(nil)
694 * second = g:getnext(first)
695 * third = g:getnext(second)
696 \*-------------------------------------------------------------------------*/
697 static int gr_getnext(lua_State
*L
)
702 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
703 if (lua_isnil(L
, 2)){
704 g
= agfstsubg(ud
->g
);
706 ud_sg
= tograph(L
, 2, STRICT
);
707 g
= agnxtsubg(ud_sg
->g
);
713 rv
= get_object(L
, g
);
718 ud_sg
= lua_newuserdata(L
, sizeof(gr_graph_t
));
720 ud_sg
->name
= strdup(agnameof(g
));
721 ud_sg
->type
= AGRAPH
;
722 ud_sg
->status
= ALIVE
;
729 /*-------------------------------------------------------------------------*\
730 * Iterator: g.walk(self)
731 * Iterator over all subgraphs of the given graph.
733 * for g in g:walk() do ... end
734 \*-------------------------------------------------------------------------*/
735 static int gr_walk(lua_State
*L
)
737 lua_pushcfunction(L
, gr_getnext
); /* ud, getnext - iterator func */
738 lua_pushvalue(L
, -2); /* ud, getnext, g - state */
739 lua_pushnil(L
); /* ud, getnext, g, nil - initial param = nil*/
743 /*-------------------------------------------------------------------------*\
744 * Property: id [number]
748 \*-------------------------------------------------------------------------*/
749 static int gr_id(lua_State
*L
)
751 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
752 lua_pushnumber(L
, AGID(ud
->g
));
756 /*-------------------------------------------------------------------------*\
757 * Property: isroot [boolean]
758 * Is given graph a root ?
761 \*-------------------------------------------------------------------------*/
762 static int gr_isroot(lua_State
*L
)
764 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
765 lua_pushboolean(L
, agroot(ud
->g
) == ud
->g
);
769 /*-------------------------------------------------------------------------*\
770 * Method: t, err = g.getgraphattr(self),
771 * t, err = g.getnodeattr(self),
772 * t, err = g.getedgeattr(self)
773 * Reads default attributes into a Lua table.
775 * tab, err = g:getnodeattr()
776 \*-------------------------------------------------------------------------*/
777 static int gr_getgraphattr(lua_State
*L
)
779 return getattr(L
, AGRAPH
);
782 static int gr_getnodeattr(lua_State
*L
)
784 return getattr(L
, AGNODE
);
787 static int gr_getedgeattr(lua_State
*L
)
789 return getattr(L
, AGEDGE
);
792 /*-------------------------------------------------------------------------*\
793 * Method: t, err = g.getattr(self)
794 * Reads declarations/default attributes into a nested Lua table for format:
795 * t = {graph={name=val, ...}, node={name=val}, edge={name=val}}
797 * tab, err = g:getattr()
798 \*-------------------------------------------------------------------------*/
799 static int gr_getattr(lua_State
*L
)
801 lua_newtable(L
); /* ud, t */
803 lua_pushstring(L
, "graph"); /* ud, t, key */
804 getattr(L
, AGRAPH
); /* ud, t, key, gt */
805 lua_settable(L
, -3); /* ud, t */
807 lua_pushstring(L
, "node"); /* ud, t, key */
808 getattr(L
, AGNODE
); /* ud, t, key, nt, n */
809 lua_settable(L
, -3); /* ud, t */
811 lua_pushstring(L
, "edge"); /* ud, t, key */
812 getattr(L
, AGEDGE
); /* ud, t, key, et, n */
813 lua_settable(L
, -3); /* ud, t */
818 /*-------------------------------------------------------------------------*\
819 * Method: n, err = g.setgraphattr(self, t),
820 * n, err = g.setnodeattr(self, t),
821 * n, err = g.setedgeattr(self, t)
822 * Sets default attributes from a Lua table t.
823 * Returns the number of attributes set.
825 * rv, err = g:setnodeattr{shape="box", width=12}
826 \*-------------------------------------------------------------------------*/
827 static int gr_setgraphattr(lua_State
*L
)
829 return setattr(L
, AGRAPH
);
832 static int gr_setnodeattr(lua_State
*L
)
834 return setattr(L
, AGNODE
);
837 static int gr_setedgeattr(lua_State
*L
)
839 return setattr(L
, AGEDGE
);
842 /*-------------------------------------------------------------------------* \
843 * Method: n, err = g.setattr(self, t)
844 * Sets default attributes from a nested Lua table of format:
845 * see g.getattr(self) above.
846 * Returns the number of attributes set.
848 * rv, err = g:setattr{graph={...}, node={shape="box"}, edge={...}}
849 \*-------------------------------------------------------------------------*/
850 static int gr_setattr(lua_State
*L
)
855 /* 1. Retrieve object specific attribute table from attribute table. */
856 lua_pushstring(L
, "graph"); /* ud, t, key */
857 lua_gettable(L
, -2); /* ud, t, gt */
858 if (!lua_isnil(L
, -1)){
859 /* 2. Set/declare all object specific attribute */
860 rv
= setattr(L
, AGRAPH
); /* ud, t, gt, n */
861 n
+= (int) lua_tonumber(L
, -rv
);
862 lua_pop(L
, 2); /* ud, t */
864 lua_pop(L
, 1); /* ud, t */
866 /* 1. ... see above */
867 lua_pushstring(L
, "node"); /* ud, t, key */
868 lua_gettable(L
, -2); /* ud, t, nt */
869 if (!lua_isnil(L
, -1)){
870 /* 2. ... see above */
871 rv
= setattr(L
, AGNODE
); /* ud, t, nt, n */
872 n
+= (int) lua_tonumber(L
, -rv
);
873 lua_pop(L
, 2); /* ud, t */
875 lua_pop(L
, 1); /* ud, t */
877 /* 1. ... see above */
878 lua_pushstring(L
, "edge"); /* ud, t, key */
879 lua_gettable(L
, -2); /* ud, t, et */
880 if (!lua_isnil(L
, -1)){
881 /* 2. ... see above */
882 rv
= setattr(L
, AGEDGE
); /* ud, t, et, n */
883 n
+= (int) lua_tonumber(L
, -rv
);
884 lua_pop(L
, 2); /* ud, t */
888 lua_pushnumber(L
, n
); /* ud, t, n */
892 /*-------------------------------------------------------------------------*\
893 * Method: g.delete(self, obj)
894 * Deletes an object from the given graph.
897 * rv, err = g:delete(n)
898 \*-------------------------------------------------------------------------*/
899 static int gr_delete(lua_State
*L
)
901 gr_object_t
*obj
= toobject(L
, 2, NULL
, STRICT
);
902 TRACE(" g.delete(): obj: ud=%p ptr=%p type=%d (%s %d)\n",
903 (void *) obj
, (void *) obj
->p
.p
, AGTYPE(obj
->p
.p
), __FILE__
, __LINE__
);
905 switch(AGTYPE(obj
->p
.p
)){
907 TRACE(" g.delete(): graph: ud=%p '%s' g=%p (%s %d)\n",
908 (void *)obj
, agnameof(obj
->g
.g
), (void *)obj
->g
.g
, __FILE__
, __LINE__
);
909 lua_pushcfunction(L
, gr_close
); /* ud, obj, func */
910 lua_pushvalue(L
, 2); /* ud, obj, func, obj */
911 lua_call(L
, 1, 1); /* ud, obj, result */
912 lua_pop(L
, 1); /* ud, obj */
915 TRACE(" g:delete(): node: ud=%p '%s' g=%p (%s %d)\n",
916 (void *)obj
, agnameof(obj
->n
.n
), (void *)obj
->n
.n
, __FILE__
, __LINE__
);
917 lua_pushcfunction(L
, gr_delete_node
);
918 lua_pushvalue(L
, 2); /* ud, obj, func, obj */
919 lua_call(L
, 1, 1); /* ud, obj, result */
920 lua_pop(L
, 1); /* ud, obj */
923 TRACE(" g:delete(): edge: ud=%p '%s' g=%p (%s %d)\n",
924 (void *)obj
, obj
->p
.name
, (void *)obj
->e
.e
, __FILE__
, __LINE__
);
925 lua_pushcfunction(L
, gr_delete_edge
);
926 lua_pushvalue(L
, 2); /* ud, obj, func, obj */
927 lua_call(L
, 1, 1); /* ud, obj, result */
928 lua_pop(L
, 1); /* ud, obj */
931 lua_pushnumber(L
, 0);
936 * A simple wrapper for usage of gr_node in other files.
938 int gr_create_node(lua_State
*L
)
943 /*-------------------------------------------------------------------------* \
944 * Method: n, err = g.node(self [, name[, nocreate]])
945 * Finds or creates a node of given name. If name is nil autoname 'node@ID'
946 * is used. The optional flag nocreate=true inhibits auto-creation.
949 * n, err = g:node("node-1", true)
950 * - Node will not be created, if it does not exists.
951 * n, err = g:node("node2")
952 * - Node will be created, if it does not exist.
955 \*-------------------------------------------------------------------------*/
956 static int gr_node(lua_State
*L
)
963 /* param 1: graph = self */
964 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
966 name
= (char *) luaL_checkstring(L
, 2); /* ud, name */
968 if (name
&& (n
= agnode(ud
->g
, name
, 0)) != NULL
){
969 rv
= get_object(L
, n
); /* ud, name, node/nil */
970 if (lua_isnil(L
, -rv
)){
971 lua_pop(L
, rv
); /* ud, name */
972 /* Node not yet registered */
973 node
= lua_newuserdata(L
, sizeof(gr_node_t
)); /* ud, name, node */
975 node
->name
= strdup(agnameof(n
));
977 node
->status
= ALIVE
;
980 /* Node already registered */
981 return rv
; /* ud, name, node */
983 /* Node does not exist */
984 if (lua_toboolean(L
, 3)){
985 /* auto-creation forbidden: return an error */
987 lua_pushstring(L
, "node not found");
990 /* Enforce creation with given name or an intermediate name '__tempname__' */
991 node
= lua_newuserdata(L
, sizeof(gr_node_t
));
992 if ( (name
&& (node
->n
= agnode(ud
->g
, name
, 1)) == NULL
)){
993 luaL_error(L
, "agnode failed");
996 node
->name
= strdup(name
);
998 node
->status
= ALIVE
;
1003 /*-------------------------------------------------------------------------* \
1004 * Method: e, tail, head = g.edge(self, tail, head, label, nocreate)
1005 * Finds or creates an edge from tail to head with label. Parameter label
1007 * The optional flag nocreate inhibits auto-creation of the edge.
1008 * Any node given by name is implicitly created and it's userdata returned
1009 * as additional results - even if nocreate is not set.
1010 * Userdata proxy object is registered on the fly, if not yet available.
1012 * e, tail, head = g:edge(n1, "node@2", "n1 => n2"[, false|true])
1013 * e, tail, head = g:edge(n1, n2)
1014 * e, tail, head = g:edge("node@1", n2, nil, true)
1015 \*-------------------------------------------------------------------------*/
1016 static int gr_edge(lua_State
*L
)
1020 gr_node_t
*tail
, *head
;
1024 int head_created
= 0;
1025 int tail_created
= 0;
1027 /* param 1: graph = self */
1028 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
1030 /* param 2: tail node */
1031 if (lua_isuserdata(L
, 2) == 1) {
1032 tail
= tonode(L
, 2, STRICT
);
1034 /* Create a node given only by name */
1035 lua_pushcfunction(L
, gr_node
); /* ud, ntail, (n)head, [label], func */
1036 lua_pushvalue(L
, 1); /* ud, ntail, (n)head, [label], func, ud */
1037 lua_pushstring(L
, (const char *) luaL_checkstring(L
, 2));
1038 /* g.node(self, name, flag) */
1039 lua_call(L
, 2, 1); /* ud, ntail, (n)head, (label), tail */
1040 if (lua_isnil(L
, -1)){
1041 return luaL_error(L
, "tailnode create failed");
1044 tail
= tonode(L
, -1, STRICT
);
1045 lua_pop(L
, 1); /* ud, ntail, (n)head, (label) */
1048 /* param 3: head node */
1049 if (lua_isuserdata(L
, 3))
1050 head
= tonode(L
, 3, STRICT
);
1052 /* Create a node given only by name */
1053 lua_pushcfunction(L
, gr_node
); /* ud, ntail, (n)head, [label], func */
1054 lua_pushvalue(L
, 1); /* ud, ntail, (n)head, [label], func, ud */
1055 lua_pushstring(L
, (const char *) luaL_checkstring(L
, 3));
1056 /* g.node(self, name, flag) */
1057 lua_call(L
, 2, 1); /* ud, ntail, (n)head, (label), head */
1058 if (lua_isnil(L
, -1)){
1059 del_object(L
, tail
);
1060 return luaL_error(L
, "headnode create failed");
1062 head
= tonode(L
, -1, STRICT
);
1063 lua_pop(L
, 1); /* ud, ntail, (n)head, (label) */
1067 /* param 4: label */
1068 label
= (char *) luaL_optstring(L
, 4, NULL
); /* ud, tail, head */
1070 /* Check whether edge already exists - only required for strict graphs */
1071 if ((e
= agedge(ud
->g
, tail
->n
, head
->n
, NULL
, 0)) != NULL
){
1072 TRACE(" gr_edge(): edge '%s' exists force=%d (%s %d)\n", agnameof(e
), (int) lua_toboolean(L
, 5), __FILE__
, __LINE__
);
1074 if (agisstrict(ud
->g
)) {
1075 /* strict directed graph: give edge a new label */
1076 agsafeset(e
, "label", label
, NULL
);
1078 rv
= get_object(L
, e
);
1079 if (lua_isnil(L
, -rv
)){
1080 rv
= get_object(L
, agopp(e
));
1081 if (lua_isnil(L
, -rv
))
1084 lua_pushlightuserdata(L
, tail
);
1085 lua_pushlightuserdata(L
, head
);
1088 /* Edge doees not exist */
1089 if (lua_isboolean(L
, 5)){
1090 /* creation of edge forbidden: nocreate flag is set */
1092 del_object(L
, tail
->n
);
1094 del_object(L
, head
->n
);
1096 lua_pushstring(L
, "edge not found");
1099 /* Enforce creation */
1100 edge
= lua_newuserdata(L
, sizeof(gr_edge_t
));
1101 if (agroot(tail
->n
) != agroot(head
->n
)){
1102 luaL_error(L
, "nodes in different graphs");
1105 sprintf(ename
, "edge@%u", newid());
1106 if (!(edge
->e
= agedge(ud
->g
, tail
->n
, head
->n
, ename
, 1))){
1107 /* creation failed */
1109 del_object(L
, tail
->n
);
1111 del_object(L
, head
->n
);
1113 lua_pushstring(L
, "agedge failed");
1116 TRACE(" gr_edge(): ud=%p '%s' l=%p g=%p e=%p tail=%p head=%p force=%d (%s %d)\n",
1117 (void *) edge
, label
, (void*) label
, (void *)ud
->g
, (void *)edge
->e
,
1118 (void *)tail
->n
, (void *)head
->n
, (int) lua_toboolean(L
, 5), __FILE__
, __LINE__
);
1120 agsafeset(edge
->e
, "label", label
, NULL
);
1121 edge
->name
= strdup(ename
);
1122 edge
->type
= AGEDGE
;
1123 edge
->status
= ALIVE
;
1125 lua_pushlightuserdata(L
, tail
);
1126 lua_pushlightuserdata(L
, head
);
1130 /*-------------------------------------------------------------------------*\
1131 * Method: n, err = g.findedge(self, tail, head [, name])
1132 * Finds an edge of given name between given nodes.
1133 * If multiple edges exist in non-directed graph, onle one of them is
1134 * retrieved, if name is not specified.
1135 * Returns proxy userdata.
1137 * e, tail, head = g:findedge(n1, "node@2", "edge@122")
1138 \*-------------------------------------------------------------------------*/
1139 static int gr_findedge(lua_State
*L
)
1145 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
1146 gr_node_t
*tail
= tonode(L
, 2, STRICT
);
1147 gr_node_t
*head
= tonode(L
, 3, STRICT
);
1148 char *name
= (char *) luaL_optstring(L
, 4, NULL
); /* ud, tail, head, label */
1149 if ((e
= agedge(ud
->g
, tail
->n
, head
->n
, name
, 0)) != NULL
){
1150 rv
= get_object(L
, e
); /* ud, tail, head, (label), e/nil */
1151 if (lua_isnil(L
, -rv
)){
1152 rv
= get_object(L
, agopp(e
)); /* ud, tail, head, (label), e/nil, nil/e */
1153 if (lua_isnil(L
, -rv
)){
1154 lua_pop(L
, rv
); /* ud, tail, head, (label) */
1155 /* Edge not yet registered */
1156 edge
= lua_newuserdata(L
, sizeof(gr_edge_t
)); /* ud, peer, name, edge */
1158 sprintf(sbuf
, "edge@%lu", (unsigned long) AGID(e
));
1159 edge
->name
= strdup(sbuf
);
1160 edge
->type
= AGEDGE
;
1161 edge
->status
= ALIVE
;
1162 set_object(L
, e
); /* ud, peer, name, edge */
1164 lua_pushlightuserdata(L
, tail
);
1165 lua_pushlightuserdata(L
, head
);
1168 /* Edge already registered */
1169 lua_pushlightuserdata(L
, tail
);
1170 lua_pushlightuserdata(L
, head
);
1171 return rv
+ 2; /* ud, peer, name, edge */
1174 /* Edge already registered */
1175 lua_pushlightuserdata(L
, tail
);
1176 lua_pushlightuserdata(L
, head
);
1177 return rv
+ 2; /* ud, peer, name, edge */
1181 lua_pushstring(L
, "edge not found");
1185 /*-------------------------------------------------------------------------*\
1186 * Method: n, err = g.idnode(self, id)
1187 * Finds a node by id.
1190 * n, err = g:idnode(18)
1191 \*-------------------------------------------------------------------------*/
1192 static int gr_idnode(lua_State
*L
)
1195 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
1196 unsigned long id
= (unsigned long) luaL_checknumber(L
, 2);
1197 if ((n
= agidnode(ud
->g
, id
, 0)) != NULL
){
1198 return get_object(L
, n
);
1201 lua_pushstring(L
, "agidnode failed");
1206 /*-------------------------------------------------------------------------*\
1207 * Method: n = nextnode(self, prev)
1208 * Retrieves next node in a graph.
1209 * Returns the successor of the given node. With nil as parameter prev the
1210 * first node is returned.
1212 * first = g:nextnode(nil)
1213 * second = g:nextnode(first)
1214 * third = g:nextnode(second)
1215 \*-------------------------------------------------------------------------*/
1216 static int gr_nextnode(lua_State
*L
)
1221 gr_graph_t
*ud_g
= tograph(L
, 1, STRICT
);
1222 if (lua_isnil(L
, 2))
1223 n
= agfstnode(ud_g
->g
);
1225 ud_n
= tonode(L
, 2, STRICT
);
1226 n
= agnxtnode(ud_g
->g
, ud_n
->n
);
1233 /* Check whether userdata exists .. */
1234 rv
= get_object(L
, n
);
1236 /* .. yes: return it */
1239 /* .. no: create it */
1241 ud_n
= lua_newuserdata(L
, sizeof(gr_node_t
));
1243 ud_n
->name
= strdup(agnameof(n
));
1244 ud_n
->type
= AGNODE
;
1245 ud_n
->status
= ALIVE
;
1252 /*-------------------------------------------------------------------------*\
1253 * Iterator: walknodes()
1254 * Iterator over all nodes of a given graph.
1256 * for n in g:walknodes() do ... end
1257 \*-------------------------------------------------------------------------*/
1258 static int gr_walknodes(lua_State
*L
)
1260 lua_pushcfunction(L
, gr_nextnode
); /* ud, nextnode */
1261 lua_pushvalue(L
, -2); /* ud, nextnode, g */
1262 lua_pushnil(L
); /* ud, nextnode, g, nil */
1266 /*-------------------------------------------------------------------------*\
1267 * Property: g.graph [userdata]
1268 * Retrieves the graph to which given subgraph belongs. Trivial because it
1270 * Returns graph userdata.
1273 \*-------------------------------------------------------------------------*/
1274 int gr_graphof(lua_State
*L
)
1276 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
1277 Agraph_t
*g
= agraphof(ud
->g
);
1280 lua_pushstring(L
, "no graph");
1283 return get_object(L
, ud
->g
);
1286 /*-------------------------------------------------------------------------*\
1287 * Method: rv = g.contains(self, obj)
1288 * Returns true if graph g contains object obj.
1291 \*-------------------------------------------------------------------------*/
1292 int gr_contains(lua_State
*L
)
1294 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
1295 gr_object_t
*obj
= toobject(L
, 2, NULL
, STRICT
);
1296 int rv
= agcontains(ud
->g
, obj
->p
.p
);
1297 if (rv
&& AGTYPE(obj
->p
.p
) == AGNODE
){
1298 if (agroot(obj
->n
.n
) == agroot(ud
->g
))
1299 lua_pushboolean(L
, TRUE
);
1301 lua_pushboolean(L
, FALSE
);
1303 lua_pushboolean(L
, rv
);
1307 /*-------------------------------------------------------------------------*\
1308 * Method: rv = g.layout(self, fmt)
1309 * Layout the given graph in the specified format/algorithm.
1311 * b = g:layout("dot")
1312 \*-------------------------------------------------------------------------*/
1313 static int gr_layout(lua_State
*L
)
1316 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
1317 char *fmt
= (char *) luaL_optstring(L
, 2, "dot");
1319 if (!strcmp(fmt
, "dot") ||
1320 !strcmp(fmt
, "neato") ||
1321 !strcmp(fmt
, "nop") ||
1322 !strcmp(fmt
, "nop2") ||
1323 !strcmp(fmt
, "twopi") ||
1324 !strcmp(fmt
, "fdp") ||
1325 !strcmp(fmt
, "circo")){
1327 if ((rv
= gv_layout(ud
->g
, fmt
)) != GR_SUCCESS
){
1328 luaL_error(L
, "layout error: %d", rv
);
1331 lua_pushvalue(L
, rv
);
1334 luaL_error(L
, "invalid layout format '%s'", fmt
);
1339 /*-------------------------------------------------------------------------*\
1340 * Method: rv = g.freelayout(self, fmt)
1343 * b = g:freelayout()
1344 \*-------------------------------------------------------------------------*/
1345 static int gr_freelayout(lua_State
*L
)
1347 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
1349 gv_free_layout(ud
->g
);
1352 lua_pushstring(L
, "invalid graph");
1355 lua_pushnumber(L
, 0);
1358 /*-------------------------------------------------------------------------*\
1359 * Method: rv = g.render(self, rfmt, file, lfmt)
1360 * Render the given graph in the specified format.
1362 * b = g:render("pdf")
1363 \*-------------------------------------------------------------------------*/
1364 static int gr_render(lua_State
*L
)
1367 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
1368 char *rfmt
= (char *) luaL_optstring(L
, 2, "plain");
1369 char *fname
= (char *) luaL_optstring(L
, 3, NULL
);
1370 char *lfmt
= (char *) luaL_optstring(L
, 4, NULL
);
1373 lua_pushstring(L
, "layout missing");
1377 gv_layout(ud
->g
, lfmt
);
1379 rv
= gv_render_file(ud
->g
, rfmt
, fname
);
1381 rv
= gv_render(ud
->g
, rfmt
, stdout
);
1383 gv_free_layout(ud
->g
);
1384 if (rv
!= GR_SUCCESS
){
1386 lua_pushstring(L
, "gvRender failed");
1389 lua_pushnumber(L
, rv
);
1393 /*-------------------------------------------------------------------------*\
1394 * Method: list, count = graph.plugins(type)
1395 * Retrieve available plugins for layout or rendering.
1396 * type: layout | render
1398 * tab, err = g:layout("layout")
1399 \*-------------------------------------------------------------------------*/
1400 static int gr_plugins(lua_State
*L
)
1405 char *kind
= (char *) luaL_optstring(L
, 1, "render");
1406 list
= gvPluginList(gvc
, kind
, &count
, NULL
);
1409 lua_pushstring(L
, "no plugins");
1413 lua_newtable(L
); /* t */
1414 for (i
= 0; i
< count
; i
++){
1415 lua_pushnumber(L
, i
+1); /* t, index */
1416 lua_pushstring(L
, list
[i
]); /* t, index, value */
1417 lua_settable(L
, -3); /* t */
1419 lua_pushnumber(L
, count
);
1420 for (i
= 0; i
< count
; i
++)
1425 /*-------------------------------------------------------------------------*\
1426 * Module initialization
1427 \*-------------------------------------------------------------------------*/
1428 LUALIB_API
int luaopen_graph_core(lua_State
*L
) {
1430 lua_pushvalue(L
, -1);
1431 lua_setglobal(L
, MYNAME
);
1432 register_metainfo(L
, funcs
);
1433 lua_pushliteral(L
, "version");
1434 lua_pushliteral(L
, MYVERSION
);
1436 lua_pushliteral(L
, "_VERSION");
1437 lua_pushliteral(L
, MYVERSION
);
1439 lua_pushliteral(L
, "_GVVERSION");
1440 lua_pushliteral(L
, MYGVVERSION
);
1442 lua_pushliteral(L
, "_COPYRIGHT");
1443 lua_pushliteral(L
, MYCOPYRIGHT
);
1445 lua_pushliteral(L
, "_DESCRIPTION");
1446 lua_pushliteral(L
, MYDESCRIPTION
);
1448 lua_pushliteral(L
, "_SYSTEM");
1449 lua_pushliteral(L
, MYSYSTEM
);
1451 lua_pushliteral(L
, "plugins");
1452 lua_pushcfunction(L
, gr_plugins
);
1454 if ((gvc
= gvContextPlugins(lt_preloaded_symbols
, DEMAND_LOADING
)) == NULL
){
1455 return luaL_error(L
, "cannot load plugins");
1460 /*-------------------------------------------------------------------------*\
1461 * Metamethod: __tostring [boolean]
1462 * Return a string presentation of a graph object.
1464 * g == h or g != h with metamethods
1465 \*-------------------------------------------------------------------------*/
1466 static int gr_tostring(lua_State
*L
)
1468 gr_object_t
*ud
= toobject(L
, 1, NULL
, NONSTRICT
);
1469 lua_pushfstring(L
, "graph: %p (%s)", ud
, ud
->p
.status
== ALIVE
? "alive" : "dead");