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.2.0"
29 #if defined(_WIN32) || defined(WIN32)
30 #define MYGVVERSION "2.0.38"
31 #define MYSYSTEM "Win32"
33 #if defined(__linux__)
34 #define MYSYSTEM "Linux"
35 #define MYGVVERSION GVVERSION
37 #define MYSYSTEM "other"
38 #define MYGVVERSION GVVERSION
41 #define MYCOPYRIGHT "Copyright (C) 2006-2024, Herbert Leuwer "
42 #define MYDESCRIPTION "LuaGRAPH is a library for creating, manipulating and rendering GRAPHs (based on the GRAPHVIZ library cgraph)."
44 #define NIL(x) ((x) NULL)
46 /*=========================================================================*\
48 \*=========================================================================*/
49 static int gr_open(lua_State
*L
);
50 static int gr_close(lua_State
*L
);
51 static int gr_nameof(lua_State
*L
);
52 static int gr_write(lua_State
*L
);
53 static int gr_read(lua_State
*L
);
54 static int gr_memread(lua_State
*L
);
55 static int gr_isstrict(lua_State
*L
);
56 static int gr_isdirected(lua_State
*L
);
57 static int gr_nnodes(lua_State
*L
);
58 static int gr_nedges(lua_State
*L
);
59 static int gr_subgraph(lua_State
*L
);
60 static int gr_root(lua_State
*L
);
61 static int gr_parent(lua_State
*L
);
62 static int gr_equal(lua_State
*L
);
63 static int gr_getnext(lua_State
*L
);
64 static int gr_walk(lua_State
*L
);
65 static int gr_id(lua_State
*L
);
66 static int gr_isroot(lua_State
*L
);
67 static int gr_getgraphattr(lua_State
*L
);
68 static int gr_getnodeattr(lua_State
*L
);
69 static int gr_getedgeattr(lua_State
*L
);
70 static int gr_getattr(lua_State
*L
);
71 static int gr_setgraphattr(lua_State
*L
);
72 static int gr_setnodeattr(lua_State
*L
);
73 static int gr_setedgeattr(lua_State
*L
);
74 static int gr_setattr(lua_State
*L
);
75 static int gr_delete(lua_State
*L
);
76 static int gr_node(lua_State
*L
);
77 static int gr_edge(lua_State
*L
);
78 static int gr_findedge(lua_State
*L
);
79 static int gr_idnode(lua_State
*L
);
80 static int gr_nextnode(lua_State
*L
);
81 static int gr_walknodes(lua_State
*L
);
82 static int gr_graphof(lua_State
*L
);
83 static int gr_contains(lua_State
*L
);
84 static int gr_layout(lua_State
*L
);
85 static int gr_freelayout(lua_State
*L
);
86 static int gr_render(lua_State
*L
);
87 static int gr_plugins(lua_State
*L
);
88 static int gr_tostring(lua_State
*L
);
90 /*=========================================================================*\
92 \*=========================================================================*/
95 #ifdef USE_BUILTIN_PLUGINS
96 extern gvplugin_library_t gvplugin_dot_layout_LTX_library
;
97 extern gvplugin_library_t gvplugin_neato_layout_LTX_library
;
98 extern gvplugin_library_t gvplugin_gd_LTX_library
;
99 extern gvplugin_library_t gvplugin_pango_LTX_library
;
101 extern gvplugin_library_t gvplugin_webp_LTX_library
;
103 extern gvplugin_library_t gvplugin_core_LTX_library
;
105 lt_symlist_t lt_preloaded_symbols
[] = {
106 { "gvplugin_dot_layout_LTX_library", (void*)(&gvplugin_dot_layout_LTX_library
) },
107 { "gvplugin_neato_layout_LTX_library", (void*)(&gvplugin_neato_layout_LTX_library
) },
108 { "gvplugin_pango_LTX_library", (void*)(&gvplugin_pango_LTX_library
) },
110 { "gvplugin_webp_LTX_library", (void*)(&gvplugin_webp_LTX_library
) },
112 { "gvplugin_gd_LTX_library", (void*)(&gvplugin_gd_LTX_library
) },
113 { "gvplugin_core_LTX_library", (void*)(&gvplugin_core_LTX_library
) },
117 lt_symlist_t lt_preloaded_symbols
[] = {
122 * Base library functions
124 static const luaL_Reg funcs
[] = {
128 {"memread", gr_memread
},
134 * Graph object methods
136 static const luaL_Reg reg_methods
[] = {
139 {"subgraph", gr_subgraph
},
140 {"getnext", gr_getnext
},
141 {"nextgraph", gr_getnext
},
143 {"walkgraphs", gr_walk
},
144 {"getgraphattr", gr_getgraphattr
},
145 {"getnodeattr", gr_getnodeattr
},
146 {"getedgeattr", gr_getedgeattr
},
147 {"getattr", gr_getattr
},
148 {"defaults", gr_getattr
},
149 {"setgraphattr", gr_setgraphattr
},
150 {"setnodeattr", gr_setnodeattr
},
151 {"setedgeattr", gr_setedgeattr
},
152 {"setattr", gr_setattr
},
153 {"declare", gr_setattr
},
154 {"delete", gr_delete
},
157 {"findedge", gr_findedge
},
158 {"idnode", gr_idnode
},
159 {"nextnode", gr_nextnode
},
160 {"walknodes", gr_walknodes
},
161 {"type", get_object_type
},
162 {"contains", gr_contains
},
163 {"layout", gr_layout
},
164 {"freelayout", gr_freelayout
},
165 {"render", gr_render
},
173 static const luaL_Reg reg_rmembers
[] = {
174 {"nnodes", gr_nnodes
},
175 {"nedges", gr_nedges
},
177 {"status", gr_status
},
179 {"parent", gr_parent
},
180 {"isstrict", gr_isstrict
},
181 {"isdirected", gr_isdirected
},
182 {"isroot", gr_isroot
},
183 {"graph", gr_graphof
},
189 * Standard metamethods
191 static const luaL_Reg reg_metamethods
[] = {
192 {"__gc", gr_collect
},
194 {"__newindex", object_newindex_handler
},
195 {"__tostring", gr_tostring
},
200 * Event callback functions - used for registring/unregstring proxy objects
203 static const struct Agcbdisc_s disc
= {
204 { cb_insert
, cb_modify
, cb_delete
}, /* graph callbacks */
205 { cb_insert
, cb_modify
, cb_delete
}, /* node callbacks */
206 { cb_insert
, cb_modify
, cb_delete
}, /* edge callbacks */
209 /*=========================================================================*\
211 \*=========================================================================*/
212 /*-------------------------------------------------------------------------*\
214 \*-------------------------------------------------------------------------*/
217 * Create a new Lua userdata object and configure metatable.
219 static int new_graph(lua_State
*L
){
220 return new_object(L
, "graph", reg_rmembers
, reg_methods
,
221 reg_metamethods
, object_index_handler
);
224 #define DEMAND_LOADING (1)
227 * Layout a graph using given engine.
229 static int gv_layout(Agraph_t
*g
, const char *engine
)
231 int rv
= gvLayout(gvc
, g
, engine
);
237 static int gv_free_layout(Agraph_t
*g
)
239 int rv
= gvFreeLayout(gvc
, g
);
246 * Render layouted graph into a file given by file handle
247 * using given format.
249 static int gv_render(Agraph_t
*g
, const char *fmt
, FILE *fout
)
251 int rv
= gvRender(gvc
, g
, fmt
, fout
);
257 * Render layouted graph into a file given by file name using given
260 static int gv_render_file(Agraph_t
*g
, const char *fmt
, const char *fname
)
262 int rv
= gvRenderFilename(gvc
, g
, fmt
, fname
);
269 * Gets attributes for a specific object type into a table rt.
270 * If no attributes are defined, the function returns an empty table.
271 * Lua entry stack: ud
272 * Lua exit stack: ud, rt
274 static int getattr(lua_State
*L
, int kind
)
276 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
279 lua_newtable(L
); /* rt */
284 while ((sym
= agnxtattr(ud
->g
, kind
, sym
)) != NULL
){
285 lua_pushstring(L
, sym
->name
); /* rt, key */
286 lua_pushstring(L
, sym
->defval
); /* rt, key, value */
287 lua_settable(L
, -3); /* rt */
292 return luaL_error(L
, "invalid kind");
298 * Sets attributes for a specific object type from a table.
299 * Lua entry stack: ud, t
300 * Lua exit stack: ud, t
302 static int setattr(lua_State
*L
, int kind
)
307 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
309 /* Parameter check */
310 luaL_checktype(L
, -1, LUA_TTABLE
);
312 /* traverse through all key/value pairs of given table */
313 lua_pushnil(L
); /* t, nil */
314 while (lua_next(L
, -2)){ /* t, key, val */
315 if (!lua_isstring(L
, -2)){
316 lua_pop(L
, 2); /* t */
317 luaL_error(L
, "invalid key");
320 key
= (char *) lua_tostring(L
, -2);
321 value
= (char *) lua_tostring(L
, -1);
324 luaL_error(L
, "invalid value");
327 lua_pop(L
, 1); /* t, key */
329 agattr(ud
->g
, kind
, key
, value
);
331 lua_pushnumber(L
, n
);
335 /*-------------------------------------------------------------------------* \
336 * Function: g, err = graph.open(name [,kind])
338 * Returns graph userdata.
340 * g, err = graph.open(name[, kind])
341 \*-------------------------------------------------------------------------*/
342 static int gr_open(lua_State
*L
)
345 Agdesc_t kind
= Agdirected
;
346 char *name
= (char *) luaL_checkstring(L
, 1);
347 char *skind
= (char *) luaL_optstring(L
, 2, "directed");
349 if (!strcmp(skind
, "directed")) {
351 } else if (!strcmp(skind
, "strictdirected")){
352 kind
= Agstrictdirected
;
353 } else if (!strcmp(skind
, "undirected")) {
355 } else if (!strcmp(skind
, "strictundirected")) {
356 kind
= Agstrictundirected
;
358 luaL_error(L
, "invalid graph attribute");
361 /* Create a userdata */
362 ud
= lua_newuserdata(L
, sizeof(gr_graph_t
)); /* ud */
363 if (!(ud
->g
= agopen(name
, kind
, &AgDefaultDisc
))){
365 lua_pushstring(L
, "open failed");
368 ud
->name
= strdup(name
);
371 /* We need a few default fields */
372 if (!agattr(ud
->g
, AGEDGE
, "label", "") ||
373 !agattr(ud
->g
, AGRAPH
, "__attrib__", "") ||
374 !agattr(ud
->g
, AGEDGE
, "__attrib__", "") ||
375 !agattr(ud
->g
, AGNODE
, "__attrib__", "")){
376 luaL_error(L
, "declaration failed");
380 /* We set callbacks only in root graph */
381 if (ud
->g
== agroot(ud
->g
)){
382 agpushdisc(ud
->g
, (struct Agcbdisc_s
*)&disc
, L
);
385 set_object(L
, ud
->g
);
389 /*-------------------------------------------------------------------------*\
390 * Method: g.close(self)
394 * rv = graph.close(g)
396 \*-------------------------------------------------------------------------*/
397 static int gr_close(lua_State
*L
)
399 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
403 if (ud
->status
== ALIVE
) {
404 /* Delete the graph, if it still exists */
405 TRACE(" g:close(): graph: ud=%p '%s' ptr=%p type=%d (%s %d)\n",
406 (void *) ud
, agnameof(ud
->g
), (void *)ud
->g
, AGTYPE(ud
->g
), __FILE__
, __LINE__
);
410 TRACE(" g:close(): graph: ud=%p already closed (%s %d)\n", (void *) ud
, __FILE__
, __LINE__
);
412 lua_pushnumber(L
, 0);
417 /*-------------------------------------------------------------------------*\
418 * Property: name [string]
419 * Returns the name of a graph.
422 \*-------------------------------------------------------------------------*/
423 static int gr_nameof(lua_State
*L
)
425 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
426 if (ud
->status
!= ALIVE
){
427 luaL_error(L
, "deleted");
430 lua_pushstring(L
, agnameof(ud
->g
));
434 /*-------------------------------------------------------------------------*\
435 * Property: isstrict [boolean]
436 * Returns true is graph is strict, otherwise it returns false.
439 \*-------------------------------------------------------------------------*/
440 static int gr_isstrict(lua_State
*L
)
442 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
443 lua_pushboolean(L
, agisstrict(ud
->g
));
447 /*-------------------------------------------------------------------------*\
448 * Property: isdirected [boolean]
449 * Returns true if the graph is directed, otherwise it returns false
452 \*-------------------------------------------------------------------------*/
453 static int gr_isdirected(lua_State
*L
)
455 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
456 lua_pushboolean(L
, agisdirected(ud
->g
));
460 /*-------------------------------------------------------------------------* \
461 * Read a graph from a string
462 * Returns graph userdata.
464 * g, err = graph.memread(str)
465 \*-------------------------------------------------------------------------*/
466 static int gr_memread(lua_State
*L
)
469 char *str
= (char *)lua_tostring(L
, 1);
471 ud
= lua_newuserdata(L
, sizeof(gr_graph_t
));
472 if (!(ud
->g
= agmemread(str
))){
474 lua_pushstring(L
, "agread failed");
477 ud
->name
= strdup(agnameof(ud
->g
));
481 /* We set callbacks only in root graph */
482 if (ud
->g
== agroot(ud
->g
)){
483 agpushdisc(ud
->g
, (struct Agcbdisc_s
*)&disc
, L
);
485 set_object(L
, ud
->g
);
489 /*-------------------------------------------------------------------------* \
490 * Read a graph from a file; "stdin" reads from STDIN.
491 * Returns graph userdata.
493 * g, err = graph.read(filename)
494 \*-------------------------------------------------------------------------*/
495 static int gr_read(lua_State
*L
)
499 char *fname
= (char *)luaL_optstring(L
, 1, "stdin");
501 if (!strcmp(fname
, "stdin")){
504 fin
= fopen(fname
, "r");
508 lua_pushstring(L
, "fopen failed");
511 /* Create a userdata */
512 ud
= lua_newuserdata(L
, sizeof(gr_graph_t
));
513 if (!(ud
->g
= agread(fin
, NIL(Agdisc_t
*)))){
516 lua_pushstring(L
, "agread failed");
520 ud
->name
= strdup(agnameof(ud
->g
));
524 /* We set callbacks only in root graph */
525 if (ud
->g
== agroot(ud
->g
)){
526 agpushdisc(ud
->g
, (struct Agcbdisc_s
*)&disc
, L
);
528 set_object(L
, ud
->g
);
532 /*-------------------------------------------------------------------------*\
533 * Method: g.write(self, filename)
534 * Write a graph into a file.
535 * Returns 1 on success, nil plus error message on failure.
536 * rv, err = g:write(filename)
537 \*-------------------------------------------------------------------------*/
538 static int gr_write(lua_State
*L
)
543 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
544 char *fname
= (char*) luaL_optstring(L
, 2, "__std__");
546 if (!strcmp(fname
, "__std__")){
550 fout
= fopen(fname
, "w+");
554 lua_pushstring(L
, "fopen failed");
557 rv
= agwrite(ud
->g
, fout
);
562 lua_pushstring(L
, "agwrite failed");
567 lua_pushnumber(L
, rv
);
571 /*-------------------------------------------------------------------------*\
572 * Property:nnodes [number]
573 * Provides the numebr of edges of a graph
576 \*-------------------------------------------------------------------------*/
577 static int gr_nnodes(lua_State
*L
)
579 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
580 lua_pushnumber(L
, agnnodes(ud
->g
));
584 /*-------------------------------------------------------------------------*\
585 * Property: nedges [number]
586 * Provides the number of edges of a graph.
589 \*-------------------------------------------------------------------------*/
590 static int gr_nedges(lua_State
*L
)
592 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
593 lua_pushnumber(L
, agnedges(ud
->g
));
597 /*-------------------------------------------------------------------------*\
598 * Metamethod: __eq [boolean]
601 * g == h or g != h with metamethods
602 \*-------------------------------------------------------------------------*/
603 static int gr_equal(lua_State
*L
)
605 gr_graph_t
*ud1
= tograph(L
, 1, STRICT
);
606 gr_graph_t
*ud2
= tograph(L
, 2, STRICT
);
607 lua_pushboolean(L
, (ud1
->g
== ud2
->g
));
611 /*-------------------------------------------------------------------------*\
612 * Method: g.subgraph(self, name, nocreate)
613 * Find or create a subgraph. The optional flag nocreate=true inhibits creation.
614 * Returns graph userdata.
616 * sg, err = g:subgraph(name)
617 \*-------------------------------------------------------------------------*/
618 static int gr_subgraph(lua_State
*L
)
623 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
624 char *name
= (char *) luaL_checkstring(L
, 2);
626 /* Check wheter the subgraph already exists */
627 if ((g
= agsubg(ud
->g
, name
, 0)) != NULL
){
629 /* Yes - return corresponing userdata */
630 rv
= get_object(L
, g
);
631 if (lua_isnil(L
, -rv
)){
632 sg
= lua_newuserdata(L
, sizeof(gr_graph_t
));
634 sg
->name
= strdup(name
);
641 /* No - Create a new one */
642 if (lua_toboolean(L
, 3)){
644 lua_pushstring(L
, "graph not found");
647 sg
= lua_newuserdata(L
, sizeof(gr_graph_t
));
648 if (!(sg
->g
= agsubg(ud
->g
, name
, 1))){
650 lua_pushstring(L
, "agsubg failed");
654 sg
->name
= strdup(name
);
661 /*-------------------------------------------------------------------------*\
662 * Property: root [userdata]
666 \*-------------------------------------------------------------------------*/
667 static int gr_root(lua_State
*L
)
669 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
672 if (!(g
= agroot(ud
->g
))){
674 lua_pushstring(L
, "agroot failed");
676 lua_pop(L
, 1); /* empty */
677 return get_object(L
, g
); /* root */
680 /*-------------------------------------------------------------------------*\
681 * Property: parent [userdata]
682 * Get parent of graph.
685 \*-------------------------------------------------------------------------*/
686 static int gr_parent(lua_State
*L
)
688 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
689 Agraph_t
*g
= agparent(ud
->g
);
691 return get_object(L
, g
);
694 /*-------------------------------------------------------------------------*\
695 * Method: g = getnext(self, prev)
696 * Retrieves next subgraph in a graph hierarchy.
697 * Returns the successor of the given graph. With nil for parameter prev the
698 * first successor is returned.
700 * first = g:getnext(nil)
701 * second = g:getnext(first)
702 * third = g:getnext(second)
703 \*-------------------------------------------------------------------------*/
704 static int gr_getnext(lua_State
*L
)
709 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
710 if (lua_isnil(L
, 2)){
711 g
= agfstsubg(ud
->g
);
713 ud_sg
= tograph(L
, 2, STRICT
);
714 g
= agnxtsubg(ud_sg
->g
);
720 rv
= get_object(L
, g
);
725 ud_sg
= lua_newuserdata(L
, sizeof(gr_graph_t
));
727 ud_sg
->name
= strdup(agnameof(g
));
728 ud_sg
->type
= AGRAPH
;
729 ud_sg
->status
= ALIVE
;
736 /*-------------------------------------------------------------------------*\
737 * Iterator: g.walk(self)
738 * Iterator over all subgraphs of the given graph.
740 * for g in g:walk() do ... end
741 \*-------------------------------------------------------------------------*/
742 static int gr_walk(lua_State
*L
)
744 lua_pushcfunction(L
, gr_getnext
); /* ud, getnext - iterator func */
745 lua_pushvalue(L
, -2); /* ud, getnext, g - state */
746 lua_pushnil(L
); /* ud, getnext, g, nil - initial param = nil*/
750 /*-------------------------------------------------------------------------*\
751 * Property: id [number]
755 \*-------------------------------------------------------------------------*/
756 static int gr_id(lua_State
*L
)
758 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
759 lua_pushnumber(L
, AGID(ud
->g
));
763 /*-------------------------------------------------------------------------*\
764 * Property: isroot [boolean]
765 * Is given graph a root ?
768 \*-------------------------------------------------------------------------*/
769 static int gr_isroot(lua_State
*L
)
771 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
772 lua_pushboolean(L
, agroot(ud
->g
) == ud
->g
);
776 /*-------------------------------------------------------------------------*\
777 * Method: t, err = g.getgraphattr(self),
778 * t, err = g.getnodeattr(self),
779 * t, err = g.getedgeattr(self)
780 * Reads default attributes into a Lua table.
782 * tab, err = g:getnodeattr()
783 \*-------------------------------------------------------------------------*/
784 static int gr_getgraphattr(lua_State
*L
)
786 return getattr(L
, AGRAPH
);
789 static int gr_getnodeattr(lua_State
*L
)
791 return getattr(L
, AGNODE
);
794 static int gr_getedgeattr(lua_State
*L
)
796 return getattr(L
, AGEDGE
);
799 /*-------------------------------------------------------------------------*\
800 * Method: t, err = g.getattr(self)
801 * Reads declarations/default attributes into a nested Lua table for format:
802 * t = {graph={name=val, ...}, node={name=val}, edge={name=val}}
804 * tab, err = g:getattr()
805 \*-------------------------------------------------------------------------*/
806 static int gr_getattr(lua_State
*L
)
808 lua_newtable(L
); /* ud, t */
810 lua_pushstring(L
, "graph"); /* ud, t, key */
811 getattr(L
, AGRAPH
); /* ud, t, key, gt */
812 lua_settable(L
, -3); /* ud, t */
814 lua_pushstring(L
, "node"); /* ud, t, key */
815 getattr(L
, AGNODE
); /* ud, t, key, nt, n */
816 lua_settable(L
, -3); /* ud, t */
818 lua_pushstring(L
, "edge"); /* ud, t, key */
819 getattr(L
, AGEDGE
); /* ud, t, key, et, n */
820 lua_settable(L
, -3); /* ud, t */
825 /*-------------------------------------------------------------------------*\
826 * Method: n, err = g.setgraphattr(self, t),
827 * n, err = g.setnodeattr(self, t),
828 * n, err = g.setedgeattr(self, t)
829 * Sets default attributes from a Lua table t.
830 * Returns the number of attributes set.
832 * rv, err = g:setnodeattr{shape="box", width=12}
833 \*-------------------------------------------------------------------------*/
834 static int gr_setgraphattr(lua_State
*L
)
836 return setattr(L
, AGRAPH
);
839 static int gr_setnodeattr(lua_State
*L
)
841 return setattr(L
, AGNODE
);
844 static int gr_setedgeattr(lua_State
*L
)
846 return setattr(L
, AGEDGE
);
849 /*-------------------------------------------------------------------------* \
850 * Method: n, err = g.setattr(self, t)
851 * Sets default attributes from a nested Lua table of format:
852 * see g.getattr(self) above.
853 * Returns the number of attributes set.
855 * rv, err = g:setattr{graph={...}, node={shape="box"}, edge={...}}
856 \*-------------------------------------------------------------------------*/
857 static int gr_setattr(lua_State
*L
)
862 /* 1. Retrieve object specific attribute table from attribute table. */
863 lua_pushstring(L
, "graph"); /* ud, t, key */
864 lua_gettable(L
, -2); /* ud, t, gt */
865 if (!lua_isnil(L
, -1)){
866 /* 2. Set/declare all object specific attribute */
867 rv
= setattr(L
, AGRAPH
); /* ud, t, gt, n */
868 n
+= (int) lua_tonumber(L
, -rv
);
869 lua_pop(L
, 2); /* ud, t */
871 lua_pop(L
, 1); /* ud, t */
873 /* 1. ... see above */
874 lua_pushstring(L
, "node"); /* ud, t, key */
875 lua_gettable(L
, -2); /* ud, t, nt */
876 if (!lua_isnil(L
, -1)){
877 /* 2. ... see above */
878 rv
= setattr(L
, AGNODE
); /* ud, t, nt, n */
879 n
+= (int) lua_tonumber(L
, -rv
);
880 lua_pop(L
, 2); /* ud, t */
882 lua_pop(L
, 1); /* ud, t */
884 /* 1. ... see above */
885 lua_pushstring(L
, "edge"); /* ud, t, key */
886 lua_gettable(L
, -2); /* ud, t, et */
887 if (!lua_isnil(L
, -1)){
888 /* 2. ... see above */
889 rv
= setattr(L
, AGEDGE
); /* ud, t, et, n */
890 n
+= (int) lua_tonumber(L
, -rv
);
891 lua_pop(L
, 2); /* ud, t */
895 lua_pushnumber(L
, n
); /* ud, t, n */
899 /*-------------------------------------------------------------------------*\
900 * Method: g.delete(self, obj)
901 * Deletes an object from the given graph.
904 * rv, err = g:delete(n)
905 \*-------------------------------------------------------------------------*/
906 static int gr_delete(lua_State
*L
)
908 gr_object_t
*obj
= toobject(L
, 2, NULL
, STRICT
);
909 TRACE(" g.delete(): obj: ud=%p ptr=%p type=%d (%s %d)\n",
910 (void *) obj
, (void *) obj
->p
.p
, AGTYPE(obj
->p
.p
), __FILE__
, __LINE__
);
912 switch(AGTYPE(obj
->p
.p
)){
914 TRACE(" g.delete(): graph: ud=%p '%s' g=%p (%s %d)\n",
915 (void *)obj
, agnameof(obj
->g
.g
), (void *)obj
->g
.g
, __FILE__
, __LINE__
);
916 lua_pushcfunction(L
, gr_close
); /* ud, obj, func */
917 lua_pushvalue(L
, 2); /* ud, obj, func, obj */
918 lua_call(L
, 1, 1); /* ud, obj, result */
919 lua_pop(L
, 1); /* ud, obj */
922 TRACE(" g:delete(): node: ud=%p '%s' g=%p (%s %d)\n",
923 (void *)obj
, agnameof(obj
->n
.n
), (void *)obj
->n
.n
, __FILE__
, __LINE__
);
924 lua_pushcfunction(L
, gr_delete_node
);
925 lua_pushvalue(L
, 2); /* ud, obj, func, obj */
926 lua_call(L
, 1, 1); /* ud, obj, result */
927 lua_pop(L
, 1); /* ud, obj */
930 TRACE(" g:delete(): edge: ud=%p '%s' g=%p (%s %d)\n",
931 (void *)obj
, obj
->p
.name
, (void *)obj
->e
.e
, __FILE__
, __LINE__
);
932 lua_pushcfunction(L
, gr_delete_edge
);
933 lua_pushvalue(L
, 2); /* ud, obj, func, obj */
934 lua_call(L
, 1, 1); /* ud, obj, result */
935 lua_pop(L
, 1); /* ud, obj */
938 lua_pushnumber(L
, 0);
943 * A simple wrapper for usage of gr_node in other files.
945 int gr_create_node(lua_State
*L
)
950 /*-------------------------------------------------------------------------* \
951 * Method: n, err = g.node(self [, name[, nocreate]])
952 * Finds or creates a node of given name. If name is nil autoname 'node@ID'
953 * is used. The optional flag nocreate=true inhibits auto-creation.
956 * n, err = g:node("node-1", true)
957 * - Node will not be created, if it does not exists.
958 * n, err = g:node("node2")
959 * - Node will be created, if it does not exist.
962 \*-------------------------------------------------------------------------*/
963 static int gr_node(lua_State
*L
)
970 /* param 1: graph = self */
971 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
973 name
= (char *) luaL_checkstring(L
, 2); /* ud, name */
975 if (name
&& (n
= agnode(ud
->g
, name
, 0)) != NULL
){
976 rv
= get_object(L
, n
); /* ud, name, node/nil */
977 if (lua_isnil(L
, -rv
)){
978 lua_pop(L
, rv
); /* ud, name */
979 /* Node not yet registered */
980 node
= lua_newuserdata(L
, sizeof(gr_node_t
)); /* ud, name, node */
982 node
->name
= strdup(agnameof(n
));
984 node
->status
= ALIVE
;
987 /* Node already registered */
988 return rv
; /* ud, name, node */
990 /* Node does not exist */
991 if (lua_toboolean(L
, 3)){
992 /* auto-creation forbidden: return an error */
994 lua_pushstring(L
, "node not found");
997 /* Enforce creation with given name or an intermediate name '__tempname__' */
998 node
= lua_newuserdata(L
, sizeof(gr_node_t
));
999 if ( (name
&& (node
->n
= agnode(ud
->g
, name
, 1)) == NULL
)){
1000 luaL_error(L
, "agnode failed");
1003 node
->name
= strdup(name
);
1004 node
->type
= AGNODE
;
1005 node
->status
= ALIVE
;
1010 /*-------------------------------------------------------------------------* \
1011 * Method: e, tail, head = g.edge(self, tail, head, label, nocreate)
1012 * Finds or creates an edge from tail to head with label. Parameter label
1014 * The optional flag nocreate inhibits auto-creation of the edge.
1015 * Any node given by name is implicitly created and it's userdata returned
1016 * as additional results - even if nocreate is not set.
1017 * Userdata proxy object is registered on the fly, if not yet available.
1019 * e, tail, head = g:edge(n1, "node@2", "n1 => n2"[, false|true])
1020 * e, tail, head = g:edge(n1, n2)
1021 * e, tail, head = g:edge("node@1", n2, nil, true)
1022 \*-------------------------------------------------------------------------*/
1023 static int gr_edge(lua_State
*L
)
1027 gr_node_t
*tail
, *head
;
1031 int head_created
= 0;
1032 int tail_created
= 0;
1034 /* param 1: graph = self */
1035 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
1037 /* param 2: tail node */
1038 if (lua_isuserdata(L
, 2) == 1) {
1039 tail
= tonode(L
, 2, STRICT
);
1041 /* Create a node given only by name */
1042 lua_pushcfunction(L
, gr_node
); /* ud, ntail, (n)head, [label], func */
1043 lua_pushvalue(L
, 1); /* ud, ntail, (n)head, [label], func, ud */
1044 lua_pushstring(L
, (const char *) luaL_checkstring(L
, 2));
1045 /* g.node(self, name, flag) */
1046 lua_call(L
, 2, 1); /* ud, ntail, (n)head, (label), tail */
1047 if (lua_isnil(L
, -1)){
1048 return luaL_error(L
, "tailnode create failed");
1051 tail
= tonode(L
, -1, STRICT
);
1052 lua_pop(L
, 1); /* ud, ntail, (n)head, (label) */
1055 /* param 3: head node */
1056 if (lua_isuserdata(L
, 3))
1057 head
= tonode(L
, 3, STRICT
);
1059 /* Create a node given only by name */
1060 lua_pushcfunction(L
, gr_node
); /* ud, ntail, (n)head, [label], func */
1061 lua_pushvalue(L
, 1); /* ud, ntail, (n)head, [label], func, ud */
1062 lua_pushstring(L
, (const char *) luaL_checkstring(L
, 3));
1063 /* g.node(self, name, flag) */
1064 lua_call(L
, 2, 1); /* ud, ntail, (n)head, (label), head */
1065 if (lua_isnil(L
, -1)){
1066 del_object(L
, tail
);
1067 return luaL_error(L
, "headnode create failed");
1069 head
= tonode(L
, -1, STRICT
);
1070 lua_pop(L
, 1); /* ud, ntail, (n)head, (label) */
1074 /* param 4: label */
1075 label
= (char *) luaL_optstring(L
, 4, NULL
); /* ud, tail, head */
1077 /* Check whether edge already exists - only required for strict graphs */
1078 if ((e
= agedge(ud
->g
, tail
->n
, head
->n
, NULL
, 0)) != NULL
){
1079 TRACE(" gr_edge(): edge '%s' exists force=%d (%s %d)\n", agnameof(e
), (int) lua_toboolean(L
, 5), __FILE__
, __LINE__
);
1081 if (agisstrict(ud
->g
)) {
1082 /* strict directed graph: give edge a new label */
1083 agsafeset(e
, "label", label
, NULL
);
1085 rv
= get_object(L
, e
);
1086 if (lua_isnil(L
, -rv
)){
1087 rv
= get_object(L
, agopp(e
));
1088 if (lua_isnil(L
, -rv
))
1091 lua_pushlightuserdata(L
, tail
);
1092 lua_pushlightuserdata(L
, head
);
1095 /* Edge doees not exist */
1096 if (lua_isboolean(L
, 5)){
1097 /* creation of edge forbidden: nocreate flag is set */
1099 del_object(L
, tail
->n
);
1101 del_object(L
, head
->n
);
1103 lua_pushstring(L
, "edge not found");
1106 /* Enforce creation */
1107 edge
= lua_newuserdata(L
, sizeof(gr_edge_t
));
1108 if (agroot(tail
->n
) != agroot(head
->n
)){
1109 luaL_error(L
, "nodes in different graphs");
1112 sprintf(ename
, "edge@%u", newid());
1113 if (!(edge
->e
= agedge(ud
->g
, tail
->n
, head
->n
, ename
, 1))){
1114 /* creation failed */
1116 del_object(L
, tail
->n
);
1118 del_object(L
, head
->n
);
1120 lua_pushstring(L
, "agedge failed");
1123 TRACE(" gr_edge(): ud=%p '%s' l=%p g=%p e=%p tail=%p head=%p force=%d (%s %d)\n",
1124 (void *) edge
, label
, (void*) label
, (void *)ud
->g
, (void *)edge
->e
,
1125 (void *)tail
->n
, (void *)head
->n
, (int) lua_toboolean(L
, 5), __FILE__
, __LINE__
);
1127 agsafeset(edge
->e
, "label", label
, NULL
);
1128 edge
->name
= strdup(ename
);
1129 edge
->type
= AGEDGE
;
1130 edge
->status
= ALIVE
;
1132 lua_pushlightuserdata(L
, tail
);
1133 lua_pushlightuserdata(L
, head
);
1137 /*-------------------------------------------------------------------------*\
1138 * Method: n, err = g.findedge(self, tail, head [, name])
1139 * Finds an edge of given name between given nodes.
1140 * If multiple edges exist in non-directed graph, onle one of them is
1141 * retrieved, if name is not specified.
1142 * Returns proxy userdata.
1144 * e, tail, head = g:findedge(n1, "node@2", "edge@122")
1145 \*-------------------------------------------------------------------------*/
1146 static int gr_findedge(lua_State
*L
)
1152 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
1153 gr_node_t
*tail
= tonode(L
, 2, STRICT
);
1154 gr_node_t
*head
= tonode(L
, 3, STRICT
);
1155 char *name
= (char *) luaL_optstring(L
, 4, NULL
); /* ud, tail, head, label */
1156 if ((e
= agedge(ud
->g
, tail
->n
, head
->n
, name
, 0)) != NULL
){
1157 rv
= get_object(L
, e
); /* ud, tail, head, (label), e/nil */
1158 if (lua_isnil(L
, -rv
)){
1159 rv
= get_object(L
, agopp(e
)); /* ud, tail, head, (label), e/nil, nil/e */
1160 if (lua_isnil(L
, -rv
)){
1161 lua_pop(L
, rv
); /* ud, tail, head, (label) */
1162 /* Edge not yet registered */
1163 edge
= lua_newuserdata(L
, sizeof(gr_edge_t
)); /* ud, peer, name, edge */
1165 sprintf(sbuf
, "edge@%lu", (unsigned long) AGID(e
));
1166 edge
->name
= strdup(sbuf
);
1167 edge
->type
= AGEDGE
;
1168 edge
->status
= ALIVE
;
1169 set_object(L
, e
); /* ud, peer, name, edge */
1171 lua_pushlightuserdata(L
, tail
);
1172 lua_pushlightuserdata(L
, head
);
1175 /* Edge already registered */
1176 lua_pushlightuserdata(L
, tail
);
1177 lua_pushlightuserdata(L
, head
);
1178 return rv
+ 2; /* ud, peer, name, edge */
1181 /* Edge already registered */
1182 lua_pushlightuserdata(L
, tail
);
1183 lua_pushlightuserdata(L
, head
);
1184 return rv
+ 2; /* ud, peer, name, edge */
1188 lua_pushstring(L
, "edge not found");
1192 /*-------------------------------------------------------------------------*\
1193 * Method: n, err = g.idnode(self, id)
1194 * Finds a node by id.
1197 * n, err = g:idnode(18)
1198 \*-------------------------------------------------------------------------*/
1199 static int gr_idnode(lua_State
*L
)
1202 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
1203 unsigned long id
= (unsigned long) luaL_checknumber(L
, 2);
1204 if ((n
= agidnode(ud
->g
, id
, 0)) != NULL
){
1205 return get_object(L
, n
);
1208 lua_pushstring(L
, "agidnode failed");
1213 /*-------------------------------------------------------------------------*\
1214 * Method: n = nextnode(self, prev)
1215 * Retrieves next node in a graph.
1216 * Returns the successor of the given node. With nil as parameter prev the
1217 * first node is returned.
1219 * first = g:nextnode(nil)
1220 * second = g:nextnode(first)
1221 * third = g:nextnode(second)
1222 \*-------------------------------------------------------------------------*/
1223 static int gr_nextnode(lua_State
*L
)
1228 gr_graph_t
*ud_g
= tograph(L
, 1, STRICT
);
1229 if (lua_isnil(L
, 2))
1230 n
= agfstnode(ud_g
->g
);
1232 ud_n
= tonode(L
, 2, STRICT
);
1233 n
= agnxtnode(ud_g
->g
, ud_n
->n
);
1240 /* Check whether userdata exists .. */
1241 rv
= get_object(L
, n
);
1243 /* .. yes: return it */
1246 /* .. no: create it */
1248 ud_n
= lua_newuserdata(L
, sizeof(gr_node_t
));
1250 ud_n
->name
= strdup(agnameof(n
));
1251 ud_n
->type
= AGNODE
;
1252 ud_n
->status
= ALIVE
;
1259 /*-------------------------------------------------------------------------*\
1260 * Iterator: walknodes()
1261 * Iterator over all nodes of a given graph.
1263 * for n in g:walknodes() do ... end
1264 \*-------------------------------------------------------------------------*/
1265 static int gr_walknodes(lua_State
*L
)
1267 lua_pushcfunction(L
, gr_nextnode
); /* ud, nextnode */
1268 lua_pushvalue(L
, -2); /* ud, nextnode, g */
1269 lua_pushnil(L
); /* ud, nextnode, g, nil */
1273 /*-------------------------------------------------------------------------*\
1274 * Property: g.graph [userdata]
1275 * Retrieves the graph to which given subgraph belongs. Trivial because it
1277 * Returns graph userdata.
1280 \*-------------------------------------------------------------------------*/
1281 int gr_graphof(lua_State
*L
)
1283 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
1284 Agraph_t
*g
= agraphof(ud
->g
);
1287 lua_pushstring(L
, "no graph");
1290 return get_object(L
, ud
->g
);
1293 /*-------------------------------------------------------------------------*\
1294 * Method: rv = g.contains(self, obj)
1295 * Returns true if graph g contains object obj.
1298 \*-------------------------------------------------------------------------*/
1299 int gr_contains(lua_State
*L
)
1301 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
1302 gr_object_t
*obj
= toobject(L
, 2, NULL
, STRICT
);
1303 int rv
= agcontains(ud
->g
, obj
->p
.p
);
1304 if (rv
&& AGTYPE(obj
->p
.p
) == AGNODE
){
1305 if (agroot(obj
->n
.n
) == agroot(ud
->g
))
1306 lua_pushboolean(L
, TRUE
);
1308 lua_pushboolean(L
, FALSE
);
1310 lua_pushboolean(L
, rv
);
1314 /*-------------------------------------------------------------------------*\
1315 * Method: rv = g.layout(self, fmt)
1316 * Layout the given graph in the specified format/algorithm.
1318 * b = g:layout("dot")
1319 \*-------------------------------------------------------------------------*/
1320 static int gr_layout(lua_State
*L
)
1323 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
1324 char *fmt
= (char *) luaL_optstring(L
, 2, "dot");
1326 if (!strcmp(fmt
, "dot") ||
1327 !strcmp(fmt
, "neato") ||
1328 !strcmp(fmt
, "nop") ||
1329 !strcmp(fmt
, "nop2") ||
1330 !strcmp(fmt
, "twopi") ||
1331 !strcmp(fmt
, "fdp") ||
1332 !strcmp(fmt
, "circo")){
1334 if ((rv
= gv_layout(ud
->g
, fmt
)) != GR_SUCCESS
){
1335 luaL_error(L
, "layout error: %d", rv
);
1338 lua_pushvalue(L
, rv
);
1341 luaL_error(L
, "invalid layout format '%s'", fmt
);
1346 /*-------------------------------------------------------------------------*\
1347 * Method: rv = g.freelayout(self, fmt)
1350 * b = g:freelayout()
1351 \*-------------------------------------------------------------------------*/
1352 static int gr_freelayout(lua_State
*L
)
1354 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
1356 gv_free_layout(ud
->g
);
1359 lua_pushstring(L
, "invalid graph");
1362 lua_pushnumber(L
, 0);
1365 /*-------------------------------------------------------------------------*\
1366 * Method: rv = g.render(self, rfmt, file, lfmt)
1367 * Render the given graph in the specified format.
1369 * b = g:render("pdf")
1370 \*-------------------------------------------------------------------------*/
1371 static int gr_render(lua_State
*L
)
1374 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
1375 char *rfmt
= (char *) luaL_optstring(L
, 2, "plain");
1376 char *fname
= (char *) luaL_optstring(L
, 3, NULL
);
1377 char *lfmt
= (char *) luaL_optstring(L
, 4, NULL
);
1380 lua_pushstring(L
, "layout missing");
1384 gv_layout(ud
->g
, lfmt
);
1386 rv
= gv_render_file(ud
->g
, rfmt
, fname
);
1388 rv
= gv_render(ud
->g
, rfmt
, stdout
);
1390 gv_free_layout(ud
->g
);
1391 if (rv
!= GR_SUCCESS
){
1393 lua_pushstring(L
, "gvRender failed");
1396 lua_pushnumber(L
, rv
);
1400 /*-------------------------------------------------------------------------*\
1401 * Method: list, count = graph.plugins(type)
1402 * Retrieve available plugins for layout or rendering.
1403 * type: layout | render
1405 * tab, err = g:layout("layout")
1406 \*-------------------------------------------------------------------------*/
1407 static int gr_plugins(lua_State
*L
)
1412 char *kind
= (char *) luaL_optstring(L
, 1, "render");
1414 list
= gvPluginList(gvc
, kind
, &count
, NULL
);
1416 list
= gvPluginList(gvc
, kind
, &count
);
1420 lua_pushstring(L
, "no plugins");
1424 lua_newtable(L
); /* t */
1425 for (i
= 0; i
< count
; i
++){
1426 lua_pushnumber(L
, i
+1); /* t, index */
1427 lua_pushstring(L
, list
[i
]); /* t, index, value */
1428 lua_settable(L
, -3); /* t */
1430 lua_pushnumber(L
, count
);
1431 for (i
= 0; i
< count
; i
++)
1436 /*-------------------------------------------------------------------------*\
1437 * Module initialization
1438 \*-------------------------------------------------------------------------*/
1439 LUALIB_API
int luaopen_graph_core(lua_State
*L
) {
1441 lua_pushvalue(L
, -1);
1442 lua_setglobal(L
, MYNAME
);
1443 register_metainfo(L
, funcs
);
1444 lua_pushliteral(L
, "version");
1445 lua_pushliteral(L
, MYVERSION
);
1447 lua_pushliteral(L
, "_VERSION");
1448 lua_pushliteral(L
, MYVERSION
);
1450 lua_pushliteral(L
, "_GVVERSION");
1451 lua_pushliteral(L
, MYGVVERSION
);
1453 lua_pushliteral(L
, "_COPYRIGHT");
1454 lua_pushliteral(L
, MYCOPYRIGHT
);
1456 lua_pushliteral(L
, "_DESCRIPTION");
1457 lua_pushliteral(L
, MYDESCRIPTION
);
1459 lua_pushliteral(L
, "_SYSTEM");
1460 lua_pushliteral(L
, MYSYSTEM
);
1462 lua_pushliteral(L
, "plugins");
1463 lua_pushcfunction(L
, gr_plugins
);
1465 if ((gvc
= gvContextPlugins(lt_preloaded_symbols
, DEMAND_LOADING
)) == NULL
){
1466 return luaL_error(L
, "cannot load plugins");
1471 /*-------------------------------------------------------------------------*\
1472 * Metamethod: __tostring [boolean]
1473 * Return a string presentation of a graph object.
1475 * g == h or g != h with metamethods
1476 \*-------------------------------------------------------------------------*/
1477 static int gr_tostring(lua_State
*L
)
1479 gr_object_t
*ud
= toobject(L
, 1, NULL
, NONSTRICT
);
1480 lua_pushfstring(L
, "graph: %p (%s)", ud
, ud
->p
.status
== ALIVE
? "alive" : "dead");