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 #define MYSYSTEM "other"
34 #define MYGVVERSION GVVERSION
36 #define MYCOPYRIGHT "Copyright (C) 2006-2024, Herbert Leuwer "
37 #define MYDESCRIPTION "LuaGRAPH is a library for creating, manipulating and rendering GRAPHs (based on the GRAPHVIZ library cgraph)."
39 #define NIL(x) ((x) NULL)
40 /*=========================================================================*\
42 \*=========================================================================*/
43 static int gr_open(lua_State
*L
);
44 static int gr_close(lua_State
*L
);
45 static int gr_nameof(lua_State
*L
);
46 static int gr_write(lua_State
*L
);
47 static int gr_read(lua_State
*L
);
48 static int gr_memread(lua_State
*L
);
49 static int gr_isstrict(lua_State
*L
);
50 static int gr_isdirected(lua_State
*L
);
51 static int gr_nnodes(lua_State
*L
);
52 static int gr_nedges(lua_State
*L
);
53 static int gr_subgraph(lua_State
*L
);
54 static int gr_root(lua_State
*L
);
55 static int gr_parent(lua_State
*L
);
56 static int gr_equal(lua_State
*L
);
57 static int gr_getnext(lua_State
*L
);
58 static int gr_walk(lua_State
*L
);
59 static int gr_id(lua_State
*L
);
60 static int gr_isroot(lua_State
*L
);
61 static int gr_getgraphattr(lua_State
*L
);
62 static int gr_getnodeattr(lua_State
*L
);
63 static int gr_getedgeattr(lua_State
*L
);
64 static int gr_getattr(lua_State
*L
);
65 static int gr_setgraphattr(lua_State
*L
);
66 static int gr_setnodeattr(lua_State
*L
);
67 static int gr_setedgeattr(lua_State
*L
);
68 static int gr_setattr(lua_State
*L
);
69 static int gr_delete(lua_State
*L
);
70 static int gr_node(lua_State
*L
);
71 static int gr_edge(lua_State
*L
);
72 static int gr_findedge(lua_State
*L
);
73 static int gr_idnode(lua_State
*L
);
74 static int gr_nextnode(lua_State
*L
);
75 static int gr_walknodes(lua_State
*L
);
76 static int gr_graphof(lua_State
*L
);
77 static int gr_contains(lua_State
*L
);
78 static int gr_layout(lua_State
*L
);
79 static int gr_freelayout(lua_State
*L
);
80 static int gr_render(lua_State
*L
);
81 static int gr_plugins(lua_State
*L
);
82 static int gr_tostring(lua_State
*L
);
84 /*=========================================================================*\
86 \*=========================================================================*/
89 #ifdef USE_BUILTIN_PLUGINS
90 extern gvplugin_library_t gvplugin_dot_layout_LTX_library
;
91 extern gvplugin_library_t gvplugin_neato_layout_LTX_library
;
92 extern gvplugin_library_t gvplugin_gd_LTX_library
;
93 extern gvplugin_library_t gvplugin_pango_LTX_library
;
95 extern gvplugin_library_t gvplugin_webp_LTX_library
;
97 extern gvplugin_library_t gvplugin_core_LTX_library
;
99 lt_symlist_t lt_preloaded_symbols
[] = {
100 { "gvplugin_dot_layout_LTX_library", (void*)(&gvplugin_dot_layout_LTX_library
) },
101 { "gvplugin_neato_layout_LTX_library", (void*)(&gvplugin_neato_layout_LTX_library
) },
102 { "gvplugin_pango_LTX_library", (void*)(&gvplugin_pango_LTX_library
) },
104 { "gvplugin_webp_LTX_library", (void*)(&gvplugin_webp_LTX_library
) },
106 { "gvplugin_gd_LTX_library", (void*)(&gvplugin_gd_LTX_library
) },
107 { "gvplugin_core_LTX_library", (void*)(&gvplugin_core_LTX_library
) },
111 lt_symlist_t lt_preloaded_symbols
[] = {
116 * Base library functions
118 static const luaL_Reg funcs
[] = {
122 {"memread", gr_memread
},
128 * Graph object methods
130 static const luaL_Reg reg_methods
[] = {
133 {"subgraph", gr_subgraph
},
134 {"getnext", gr_getnext
},
135 {"nextgraph", gr_getnext
},
137 {"walkgraphs", gr_walk
},
138 {"getgraphattr", gr_getgraphattr
},
139 {"getnodeattr", gr_getnodeattr
},
140 {"getedgeattr", gr_getedgeattr
},
141 {"getattr", gr_getattr
},
142 {"defaults", gr_getattr
},
143 {"setgraphattr", gr_setgraphattr
},
144 {"setnodeattr", gr_setnodeattr
},
145 {"setedgeattr", gr_setedgeattr
},
146 {"setattr", gr_setattr
},
147 {"declare", gr_setattr
},
148 {"delete", gr_delete
},
151 {"findedge", gr_findedge
},
152 {"idnode", gr_idnode
},
153 {"nextnode", gr_nextnode
},
154 {"walknodes", gr_walknodes
},
155 {"type", get_object_type
},
156 {"contains", gr_contains
},
157 {"layout", gr_layout
},
158 {"freelayout", gr_freelayout
},
159 {"render", gr_render
},
167 static const luaL_Reg reg_rmembers
[] = {
168 {"nnodes", gr_nnodes
},
169 {"nedges", gr_nedges
},
171 {"status", gr_status
},
173 {"parent", gr_parent
},
174 {"isstrict", gr_isstrict
},
175 {"isdirected", gr_isdirected
},
176 {"isroot", gr_isroot
},
177 {"graph", gr_graphof
},
183 * Standard metamethods
185 static const luaL_Reg reg_metamethods
[] = {
186 {"__gc", gr_collect
},
188 {"__newindex", object_newindex_handler
},
189 {"__tostring", gr_tostring
},
194 * Event callback functions - used for registring/unregstring proxy objects
197 static const struct Agcbdisc_s disc
= {
198 { cb_insert
, cb_modify
, cb_delete
}, /* graph callbacks */
199 { cb_insert
, cb_modify
, cb_delete
}, /* node callbacks */
200 { cb_insert
, cb_modify
, cb_delete
}, /* edge callbacks */
203 /*=========================================================================*\
205 \*=========================================================================*/
206 /*-------------------------------------------------------------------------*\
208 \*-------------------------------------------------------------------------*/
211 * Create a new Lua userdata object and configure metatable.
213 static int new_graph(lua_State
*L
){
214 return new_object(L
, "graph", reg_rmembers
, reg_methods
,
215 reg_metamethods
, object_index_handler
);
218 #define DEMAND_LOADING (1)
221 * Layout a graph using given engine.
223 static int gv_layout(Agraph_t
*g
, const char *engine
)
225 int rv
= gvLayout(gvc
, g
, engine
);
231 static int gv_free_layout(Agraph_t
*g
)
233 int rv
= gvFreeLayout(gvc
, g
);
240 * Render layouted graph into a file given by file handle
241 * using given format.
243 static int gv_render(Agraph_t
*g
, const char *fmt
, FILE *fout
)
245 int rv
= gvRender(gvc
, g
, fmt
, fout
);
251 * Render layouted graph into a file given by file name using given
254 static int gv_render_file(Agraph_t
*g
, const char *fmt
, const char *fname
)
256 int rv
= gvRenderFilename(gvc
, g
, fmt
, fname
);
263 * Gets attributes for a specific object type into a table rt.
264 * If no attributes are defined, the function returns an empty table.
265 * Lua entry stack: ud
266 * Lua exit stack: ud, rt
268 static int getattr(lua_State
*L
, int kind
)
270 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
273 lua_newtable(L
); /* rt */
278 while ((sym
= agnxtattr(ud
->g
, kind
, sym
)) != NULL
){
279 lua_pushstring(L
, sym
->name
); /* rt, key */
280 lua_pushstring(L
, sym
->defval
); /* rt, key, value */
281 lua_settable(L
, -3); /* rt */
286 return luaL_error(L
, "invalid kind");
292 * Sets attributes for a specific object type from a table.
293 * Lua entry stack: ud, t
294 * Lua exit stack: ud, t
296 static int setattr(lua_State
*L
, int kind
)
301 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
303 /* Parameter check */
304 luaL_checktype(L
, -1, LUA_TTABLE
);
306 /* traverse through all key/value pairs of given table */
307 lua_pushnil(L
); /* t, nil */
308 while (lua_next(L
, -2)){ /* t, key, val */
309 if (!lua_isstring(L
, -2)){
310 lua_pop(L
, 2); /* t */
311 luaL_error(L
, "invalid key");
314 key
= (char *) lua_tostring(L
, -2);
315 value
= (char *) lua_tostring(L
, -1);
318 luaL_error(L
, "invalid value");
321 lua_pop(L
, 1); /* t, key */
323 agattr(ud
->g
, kind
, key
, value
);
325 lua_pushnumber(L
, n
);
329 /*-------------------------------------------------------------------------* \
330 * Function: g, err = graph.open(name [,kind])
332 * Returns graph userdata.
334 * g, err = graph.open(name[, kind])
335 \*-------------------------------------------------------------------------*/
336 static int gr_open(lua_State
*L
)
339 Agdesc_t kind
= Agdirected
;
340 char *name
= (char *) luaL_checkstring(L
, 1);
341 char *skind
= (char *) luaL_optstring(L
, 2, "directed");
343 if (!strcmp(skind
, "directed")) {
345 } else if (!strcmp(skind
, "strictdirected")){
346 kind
= Agstrictdirected
;
347 } else if (!strcmp(skind
, "undirected")) {
349 } else if (!strcmp(skind
, "strictundirected")) {
350 kind
= Agstrictundirected
;
352 luaL_error(L
, "invalid graph attribute");
355 /* Create a userdata */
356 ud
= lua_newuserdata(L
, sizeof(gr_graph_t
)); /* ud */
357 if (!(ud
->g
= agopen(name
, kind
, &AgDefaultDisc
))){
359 lua_pushstring(L
, "open failed");
362 ud
->name
= strdup(name
);
365 /* We need a few default fields */
366 if (!agattr(ud
->g
, AGEDGE
, "label", "") ||
367 !agattr(ud
->g
, AGRAPH
, "__attrib__", "") ||
368 !agattr(ud
->g
, AGEDGE
, "__attrib__", "") ||
369 !agattr(ud
->g
, AGNODE
, "__attrib__", "")){
370 luaL_error(L
, "declaration failed");
374 /* We set callbacks only in root graph */
375 if (ud
->g
== agroot(ud
->g
)){
376 agpushdisc(ud
->g
, (struct Agcbdisc_s
*)&disc
, L
);
379 set_object(L
, ud
->g
);
383 /*-------------------------------------------------------------------------*\
384 * Method: g.close(self)
388 * rv = graph.close(g)
390 \*-------------------------------------------------------------------------*/
391 static int gr_close(lua_State
*L
)
393 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
397 if (ud
->status
== ALIVE
) {
398 /* Delete the graph, if it still exists */
399 TRACE(" g:close(): graph: ud=%p '%s' ptr=%p type=%d (%s %d)\n",
400 (void *) ud
, agnameof(ud
->g
), (void *)ud
->g
, AGTYPE(ud
->g
), __FILE__
, __LINE__
);
404 TRACE(" g:close(): graph: ud=%p already closed (%s %d)\n", (void *) ud
, __FILE__
, __LINE__
);
406 lua_pushnumber(L
, 0);
411 /*-------------------------------------------------------------------------*\
412 * Property: name [string]
413 * Returns the name of a graph.
416 \*-------------------------------------------------------------------------*/
417 static int gr_nameof(lua_State
*L
)
419 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
420 if (ud
->status
!= ALIVE
){
421 luaL_error(L
, "deleted");
424 lua_pushstring(L
, agnameof(ud
->g
));
428 /*-------------------------------------------------------------------------*\
429 * Property: isstrict [boolean]
430 * Returns true is graph is strict, otherwise it returns false.
433 \*-------------------------------------------------------------------------*/
434 static int gr_isstrict(lua_State
*L
)
436 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
437 lua_pushboolean(L
, agisstrict(ud
->g
));
441 /*-------------------------------------------------------------------------*\
442 * Property: isdirected [boolean]
443 * Returns true if the graph is directed, otherwise it returns false
446 \*-------------------------------------------------------------------------*/
447 static int gr_isdirected(lua_State
*L
)
449 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
450 lua_pushboolean(L
, agisdirected(ud
->g
));
454 /*-------------------------------------------------------------------------* \
455 * Read a graph from a string
456 * Returns graph userdata.
458 * g, err = graph.memread(str)
459 \*-------------------------------------------------------------------------*/
460 static int gr_memread(lua_State
*L
)
463 char *str
= (char *)lua_tostring(L
, 1);
465 ud
= lua_newuserdata(L
, sizeof(gr_graph_t
));
466 if (!(ud
->g
= agmemread(str
))){
468 lua_pushstring(L
, "agread failed");
471 ud
->name
= strdup(agnameof(ud
->g
));
475 /* We set callbacks only in root graph */
476 if (ud
->g
== agroot(ud
->g
)){
477 agpushdisc(ud
->g
, (struct Agcbdisc_s
*)&disc
, L
);
479 set_object(L
, ud
->g
);
483 /*-------------------------------------------------------------------------* \
484 * Read a graph from a file; "stdin" reads from STDIN.
485 * Returns graph userdata.
487 * g, err = graph.read(filename)
488 \*-------------------------------------------------------------------------*/
489 static int gr_read(lua_State
*L
)
493 char *fname
= (char *)luaL_optstring(L
, 1, "stdin");
495 if (!strcmp(fname
, "stdin")){
498 fin
= fopen(fname
, "r");
502 lua_pushstring(L
, "fopen failed");
505 /* Create a userdata */
506 ud
= lua_newuserdata(L
, sizeof(gr_graph_t
));
507 if (!(ud
->g
= agread(fin
, NIL(Agdisc_t
*)))){
510 lua_pushstring(L
, "agread failed");
514 ud
->name
= strdup(agnameof(ud
->g
));
518 /* We set callbacks only in root graph */
519 if (ud
->g
== agroot(ud
->g
)){
520 agpushdisc(ud
->g
, (struct Agcbdisc_s
*)&disc
, L
);
522 set_object(L
, ud
->g
);
526 /*-------------------------------------------------------------------------*\
527 * Method: g.write(self, filename)
528 * Write a graph into a file.
529 * Returns 1 on success, nil plus error message on failure.
530 * rv, err = g:write(filename)
531 \*-------------------------------------------------------------------------*/
532 static int gr_write(lua_State
*L
)
537 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
538 char *fname
= (char*) luaL_optstring(L
, 2, "__std__");
540 if (!strcmp(fname
, "__std__")){
544 fout
= fopen(fname
, "w+");
548 lua_pushstring(L
, "fopen failed");
551 rv
= agwrite(ud
->g
, fout
);
556 lua_pushstring(L
, "agwrite failed");
561 lua_pushnumber(L
, rv
);
565 /*-------------------------------------------------------------------------*\
566 * Property:nnodes [number]
567 * Provides the numebr of edges of a graph
570 \*-------------------------------------------------------------------------*/
571 static int gr_nnodes(lua_State
*L
)
573 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
574 lua_pushnumber(L
, agnnodes(ud
->g
));
578 /*-------------------------------------------------------------------------*\
579 * Property: nedges [number]
580 * Provides the number of edges of a graph.
583 \*-------------------------------------------------------------------------*/
584 static int gr_nedges(lua_State
*L
)
586 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
587 lua_pushnumber(L
, agnedges(ud
->g
));
591 /*-------------------------------------------------------------------------*\
592 * Metamethod: __eq [boolean]
595 * g == h or g != h with metamethods
596 \*-------------------------------------------------------------------------*/
597 static int gr_equal(lua_State
*L
)
599 gr_graph_t
*ud1
= tograph(L
, 1, STRICT
);
600 gr_graph_t
*ud2
= tograph(L
, 2, STRICT
);
601 lua_pushboolean(L
, (ud1
->g
== ud2
->g
));
605 /*-------------------------------------------------------------------------*\
606 * Method: g.subgraph(self, name, nocreate)
607 * Find or create a subgraph. The optional flag nocreate=true inhibits creation.
608 * Returns graph userdata.
610 * sg, err = g:subgraph(name)
611 \*-------------------------------------------------------------------------*/
612 static int gr_subgraph(lua_State
*L
)
617 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
618 char *name
= (char *) luaL_checkstring(L
, 2);
620 /* Check wheter the subgraph already exists */
621 if ((g
= agsubg(ud
->g
, name
, 0)) != NULL
){
623 /* Yes - return corresponing userdata */
624 rv
= get_object(L
, g
);
625 if (lua_isnil(L
, -rv
)){
626 sg
= lua_newuserdata(L
, sizeof(gr_graph_t
));
628 sg
->name
= strdup(name
);
635 /* No - Create a new one */
636 if (lua_toboolean(L
, 3)){
638 lua_pushstring(L
, "graph not found");
641 sg
= lua_newuserdata(L
, sizeof(gr_graph_t
));
642 if (!(sg
->g
= agsubg(ud
->g
, name
, 1))){
644 lua_pushstring(L
, "agsubg failed");
648 sg
->name
= strdup(name
);
655 /*-------------------------------------------------------------------------*\
656 * Property: root [userdata]
660 \*-------------------------------------------------------------------------*/
661 static int gr_root(lua_State
*L
)
663 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
666 if (!(g
= agroot(ud
->g
))){
668 lua_pushstring(L
, "agroot failed");
670 lua_pop(L
, 1); /* empty */
671 return get_object(L
, g
); /* root */
674 /*-------------------------------------------------------------------------*\
675 * Property: parent [userdata]
676 * Get parent of graph.
679 \*-------------------------------------------------------------------------*/
680 static int gr_parent(lua_State
*L
)
682 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
683 Agraph_t
*g
= agparent(ud
->g
);
685 return get_object(L
, g
);
688 /*-------------------------------------------------------------------------*\
689 * Method: g = getnext(self, prev)
690 * Retrieves next subgraph in a graph hierarchy.
691 * Returns the successor of the given graph. With nil for parameter prev the
692 * first successor is returned.
694 * first = g:getnext(nil)
695 * second = g:getnext(first)
696 * third = g:getnext(second)
697 \*-------------------------------------------------------------------------*/
698 static int gr_getnext(lua_State
*L
)
703 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
704 if (lua_isnil(L
, 2)){
705 g
= agfstsubg(ud
->g
);
707 ud_sg
= tograph(L
, 2, STRICT
);
708 g
= agnxtsubg(ud_sg
->g
);
714 rv
= get_object(L
, g
);
719 ud_sg
= lua_newuserdata(L
, sizeof(gr_graph_t
));
721 ud_sg
->name
= strdup(agnameof(g
));
722 ud_sg
->type
= AGRAPH
;
723 ud_sg
->status
= ALIVE
;
730 /*-------------------------------------------------------------------------*\
731 * Iterator: g.walk(self)
732 * Iterator over all subgraphs of the given graph.
734 * for g in g:walk() do ... end
735 \*-------------------------------------------------------------------------*/
736 static int gr_walk(lua_State
*L
)
738 lua_pushcfunction(L
, gr_getnext
); /* ud, getnext - iterator func */
739 lua_pushvalue(L
, -2); /* ud, getnext, g - state */
740 lua_pushnil(L
); /* ud, getnext, g, nil - initial param = nil*/
744 /*-------------------------------------------------------------------------*\
745 * Property: id [number]
749 \*-------------------------------------------------------------------------*/
750 static int gr_id(lua_State
*L
)
752 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
753 lua_pushnumber(L
, AGID(ud
->g
));
757 /*-------------------------------------------------------------------------*\
758 * Property: isroot [boolean]
759 * Is given graph a root ?
762 \*-------------------------------------------------------------------------*/
763 static int gr_isroot(lua_State
*L
)
765 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
766 lua_pushboolean(L
, agroot(ud
->g
) == ud
->g
);
770 /*-------------------------------------------------------------------------*\
771 * Method: t, err = g.getgraphattr(self),
772 * t, err = g.getnodeattr(self),
773 * t, err = g.getedgeattr(self)
774 * Reads default attributes into a Lua table.
776 * tab, err = g:getnodeattr()
777 \*-------------------------------------------------------------------------*/
778 static int gr_getgraphattr(lua_State
*L
)
780 return getattr(L
, AGRAPH
);
783 static int gr_getnodeattr(lua_State
*L
)
785 return getattr(L
, AGNODE
);
788 static int gr_getedgeattr(lua_State
*L
)
790 return getattr(L
, AGEDGE
);
793 /*-------------------------------------------------------------------------*\
794 * Method: t, err = g.getattr(self)
795 * Reads declarations/default attributes into a nested Lua table for format:
796 * t = {graph={name=val, ...}, node={name=val}, edge={name=val}}
798 * tab, err = g:getattr()
799 \*-------------------------------------------------------------------------*/
800 static int gr_getattr(lua_State
*L
)
802 lua_newtable(L
); /* ud, t */
804 lua_pushstring(L
, "graph"); /* ud, t, key */
805 getattr(L
, AGRAPH
); /* ud, t, key, gt */
806 lua_settable(L
, -3); /* ud, t */
808 lua_pushstring(L
, "node"); /* ud, t, key */
809 getattr(L
, AGNODE
); /* ud, t, key, nt, n */
810 lua_settable(L
, -3); /* ud, t */
812 lua_pushstring(L
, "edge"); /* ud, t, key */
813 getattr(L
, AGEDGE
); /* ud, t, key, et, n */
814 lua_settable(L
, -3); /* ud, t */
819 /*-------------------------------------------------------------------------*\
820 * Method: n, err = g.setgraphattr(self, t),
821 * n, err = g.setnodeattr(self, t),
822 * n, err = g.setedgeattr(self, t)
823 * Sets default attributes from a Lua table t.
824 * Returns the number of attributes set.
826 * rv, err = g:setnodeattr{shape="box", width=12}
827 \*-------------------------------------------------------------------------*/
828 static int gr_setgraphattr(lua_State
*L
)
830 return setattr(L
, AGRAPH
);
833 static int gr_setnodeattr(lua_State
*L
)
835 return setattr(L
, AGNODE
);
838 static int gr_setedgeattr(lua_State
*L
)
840 return setattr(L
, AGEDGE
);
843 /*-------------------------------------------------------------------------* \
844 * Method: n, err = g.setattr(self, t)
845 * Sets default attributes from a nested Lua table of format:
846 * see g.getattr(self) above.
847 * Returns the number of attributes set.
849 * rv, err = g:setattr{graph={...}, node={shape="box"}, edge={...}}
850 \*-------------------------------------------------------------------------*/
851 static int gr_setattr(lua_State
*L
)
856 /* 1. Retrieve object specific attribute table from attribute table. */
857 lua_pushstring(L
, "graph"); /* ud, t, key */
858 lua_gettable(L
, -2); /* ud, t, gt */
859 if (!lua_isnil(L
, -1)){
860 /* 2. Set/declare all object specific attribute */
861 rv
= setattr(L
, AGRAPH
); /* ud, t, gt, n */
862 n
+= (int) lua_tonumber(L
, -rv
);
863 lua_pop(L
, 2); /* ud, t */
865 lua_pop(L
, 1); /* ud, t */
867 /* 1. ... see above */
868 lua_pushstring(L
, "node"); /* ud, t, key */
869 lua_gettable(L
, -2); /* ud, t, nt */
870 if (!lua_isnil(L
, -1)){
871 /* 2. ... see above */
872 rv
= setattr(L
, AGNODE
); /* ud, t, nt, n */
873 n
+= (int) lua_tonumber(L
, -rv
);
874 lua_pop(L
, 2); /* ud, t */
876 lua_pop(L
, 1); /* ud, t */
878 /* 1. ... see above */
879 lua_pushstring(L
, "edge"); /* ud, t, key */
880 lua_gettable(L
, -2); /* ud, t, et */
881 if (!lua_isnil(L
, -1)){
882 /* 2. ... see above */
883 rv
= setattr(L
, AGEDGE
); /* ud, t, et, n */
884 n
+= (int) lua_tonumber(L
, -rv
);
885 lua_pop(L
, 2); /* ud, t */
889 lua_pushnumber(L
, n
); /* ud, t, n */
893 /*-------------------------------------------------------------------------*\
894 * Method: g.delete(self, obj)
895 * Deletes an object from the given graph.
898 * rv, err = g:delete(n)
899 \*-------------------------------------------------------------------------*/
900 static int gr_delete(lua_State
*L
)
902 gr_object_t
*obj
= toobject(L
, 2, NULL
, STRICT
);
903 TRACE(" g.delete(): obj: ud=%p ptr=%p type=%d (%s %d)\n",
904 (void *) obj
, (void *) obj
->p
.p
, AGTYPE(obj
->p
.p
), __FILE__
, __LINE__
);
906 switch(AGTYPE(obj
->p
.p
)){
908 TRACE(" g.delete(): graph: ud=%p '%s' g=%p (%s %d)\n",
909 (void *)obj
, agnameof(obj
->g
.g
), (void *)obj
->g
.g
, __FILE__
, __LINE__
);
910 lua_pushcfunction(L
, gr_close
); /* ud, obj, func */
911 lua_pushvalue(L
, 2); /* ud, obj, func, obj */
912 lua_call(L
, 1, 1); /* ud, obj, result */
913 lua_pop(L
, 1); /* ud, obj */
916 TRACE(" g:delete(): node: ud=%p '%s' g=%p (%s %d)\n",
917 (void *)obj
, agnameof(obj
->n
.n
), (void *)obj
->n
.n
, __FILE__
, __LINE__
);
918 lua_pushcfunction(L
, gr_delete_node
);
919 lua_pushvalue(L
, 2); /* ud, obj, func, obj */
920 lua_call(L
, 1, 1); /* ud, obj, result */
921 lua_pop(L
, 1); /* ud, obj */
924 TRACE(" g:delete(): edge: ud=%p '%s' g=%p (%s %d)\n",
925 (void *)obj
, obj
->p
.name
, (void *)obj
->e
.e
, __FILE__
, __LINE__
);
926 lua_pushcfunction(L
, gr_delete_edge
);
927 lua_pushvalue(L
, 2); /* ud, obj, func, obj */
928 lua_call(L
, 1, 1); /* ud, obj, result */
929 lua_pop(L
, 1); /* ud, obj */
932 lua_pushnumber(L
, 0);
937 * A simple wrapper for usage of gr_node in other files.
939 int gr_create_node(lua_State
*L
)
944 /*-------------------------------------------------------------------------* \
945 * Method: n, err = g.node(self [, name[, nocreate]])
946 * Finds or creates a node of given name. If name is nil autoname 'node@ID'
947 * is used. The optional flag nocreate=true inhibits auto-creation.
950 * n, err = g:node("node-1", true)
951 * - Node will not be created, if it does not exists.
952 * n, err = g:node("node2")
953 * - Node will be created, if it does not exist.
956 \*-------------------------------------------------------------------------*/
957 static int gr_node(lua_State
*L
)
964 /* param 1: graph = self */
965 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
967 name
= (char *) luaL_checkstring(L
, 2); /* ud, name */
969 if (name
&& (n
= agnode(ud
->g
, name
, 0)) != NULL
){
970 rv
= get_object(L
, n
); /* ud, name, node/nil */
971 if (lua_isnil(L
, -rv
)){
972 lua_pop(L
, rv
); /* ud, name */
973 /* Node not yet registered */
974 node
= lua_newuserdata(L
, sizeof(gr_node_t
)); /* ud, name, node */
976 node
->name
= strdup(agnameof(n
));
978 node
->status
= ALIVE
;
981 /* Node already registered */
982 return rv
; /* ud, name, node */
984 /* Node does not exist */
985 if (lua_toboolean(L
, 3)){
986 /* auto-creation forbidden: return an error */
988 lua_pushstring(L
, "node not found");
991 /* Enforce creation with given name or an intermediate name '__tempname__' */
992 node
= lua_newuserdata(L
, sizeof(gr_node_t
));
993 if ( (name
&& (node
->n
= agnode(ud
->g
, name
, 1)) == NULL
)){
994 luaL_error(L
, "agnode failed");
997 node
->name
= strdup(name
);
999 node
->status
= ALIVE
;
1004 /*-------------------------------------------------------------------------* \
1005 * Method: e, tail, head = g.edge(self, tail, head, label, nocreate)
1006 * Finds or creates an edge from tail to head with label. Parameter label
1008 * The optional flag nocreate inhibits auto-creation of the edge.
1009 * Any node given by name is implicitly created and it's userdata returned
1010 * as additional results - even if nocreate is not set.
1011 * Userdata proxy object is registered on the fly, if not yet available.
1013 * e, tail, head = g:edge(n1, "node@2", "n1 => n2"[, false|true])
1014 * e, tail, head = g:edge(n1, n2)
1015 * e, tail, head = g:edge("node@1", n2, nil, true)
1016 \*-------------------------------------------------------------------------*/
1017 static int gr_edge(lua_State
*L
)
1021 gr_node_t
*tail
, *head
;
1025 int head_created
= 0;
1026 int tail_created
= 0;
1028 /* param 1: graph = self */
1029 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
1031 /* param 2: tail node */
1032 if (lua_isuserdata(L
, 2) == 1) {
1033 tail
= tonode(L
, 2, STRICT
);
1035 /* Create a node given only by name */
1036 lua_pushcfunction(L
, gr_node
); /* ud, ntail, (n)head, [label], func */
1037 lua_pushvalue(L
, 1); /* ud, ntail, (n)head, [label], func, ud */
1038 lua_pushstring(L
, (const char *) luaL_checkstring(L
, 2));
1039 /* g.node(self, name, flag) */
1040 lua_call(L
, 2, 1); /* ud, ntail, (n)head, (label), tail */
1041 if (lua_isnil(L
, -1)){
1042 return luaL_error(L
, "tailnode create failed");
1045 tail
= tonode(L
, -1, STRICT
);
1046 lua_pop(L
, 1); /* ud, ntail, (n)head, (label) */
1049 /* param 3: head node */
1050 if (lua_isuserdata(L
, 3))
1051 head
= tonode(L
, 3, STRICT
);
1053 /* Create a node given only by name */
1054 lua_pushcfunction(L
, gr_node
); /* ud, ntail, (n)head, [label], func */
1055 lua_pushvalue(L
, 1); /* ud, ntail, (n)head, [label], func, ud */
1056 lua_pushstring(L
, (const char *) luaL_checkstring(L
, 3));
1057 /* g.node(self, name, flag) */
1058 lua_call(L
, 2, 1); /* ud, ntail, (n)head, (label), head */
1059 if (lua_isnil(L
, -1)){
1060 del_object(L
, tail
);
1061 return luaL_error(L
, "headnode create failed");
1063 head
= tonode(L
, -1, STRICT
);
1064 lua_pop(L
, 1); /* ud, ntail, (n)head, (label) */
1068 /* param 4: label */
1069 label
= (char *) luaL_optstring(L
, 4, NULL
); /* ud, tail, head */
1071 /* Check whether edge already exists - only required for strict graphs */
1072 if ((e
= agedge(ud
->g
, tail
->n
, head
->n
, NULL
, 0)) != NULL
){
1073 TRACE(" gr_edge(): edge '%s' exists force=%d (%s %d)\n", agnameof(e
), (int) lua_toboolean(L
, 5), __FILE__
, __LINE__
);
1075 if (agisstrict(ud
->g
)) {
1076 /* strict directed graph: give edge a new label */
1077 agsafeset(e
, "label", label
, NULL
);
1079 rv
= get_object(L
, e
);
1080 if (lua_isnil(L
, -rv
)){
1081 rv
= get_object(L
, agopp(e
));
1082 if (lua_isnil(L
, -rv
))
1085 lua_pushlightuserdata(L
, tail
);
1086 lua_pushlightuserdata(L
, head
);
1089 /* Edge doees not exist */
1090 if (lua_isboolean(L
, 5)){
1091 /* creation of edge forbidden: nocreate flag is set */
1093 del_object(L
, tail
->n
);
1095 del_object(L
, head
->n
);
1097 lua_pushstring(L
, "edge not found");
1100 /* Enforce creation */
1101 edge
= lua_newuserdata(L
, sizeof(gr_edge_t
));
1102 if (agroot(tail
->n
) != agroot(head
->n
)){
1103 luaL_error(L
, "nodes in different graphs");
1106 sprintf(ename
, "edge@%u", newid());
1107 if (!(edge
->e
= agedge(ud
->g
, tail
->n
, head
->n
, ename
, 1))){
1108 /* creation failed */
1110 del_object(L
, tail
->n
);
1112 del_object(L
, head
->n
);
1114 lua_pushstring(L
, "agedge failed");
1117 TRACE(" gr_edge(): ud=%p '%s' l=%p g=%p e=%p tail=%p head=%p force=%d (%s %d)\n",
1118 (void *) edge
, label
, (void*) label
, (void *)ud
->g
, (void *)edge
->e
,
1119 (void *)tail
->n
, (void *)head
->n
, (int) lua_toboolean(L
, 5), __FILE__
, __LINE__
);
1121 agsafeset(edge
->e
, "label", label
, NULL
);
1122 edge
->name
= strdup(ename
);
1123 edge
->type
= AGEDGE
;
1124 edge
->status
= ALIVE
;
1126 lua_pushlightuserdata(L
, tail
);
1127 lua_pushlightuserdata(L
, head
);
1131 /*-------------------------------------------------------------------------*\
1132 * Method: n, err = g.findedge(self, tail, head [, name])
1133 * Finds an edge of given name between given nodes.
1134 * If multiple edges exist in non-directed graph, onle one of them is
1135 * retrieved, if name is not specified.
1136 * Returns proxy userdata.
1138 * e, tail, head = g:findedge(n1, "node@2", "edge@122")
1139 \*-------------------------------------------------------------------------*/
1140 static int gr_findedge(lua_State
*L
)
1146 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
1147 gr_node_t
*tail
= tonode(L
, 2, STRICT
);
1148 gr_node_t
*head
= tonode(L
, 3, STRICT
);
1149 char *name
= (char *) luaL_optstring(L
, 4, NULL
); /* ud, tail, head, label */
1150 if ((e
= agedge(ud
->g
, tail
->n
, head
->n
, name
, 0)) != NULL
){
1151 rv
= get_object(L
, e
); /* ud, tail, head, (label), e/nil */
1152 if (lua_isnil(L
, -rv
)){
1153 rv
= get_object(L
, agopp(e
)); /* ud, tail, head, (label), e/nil, nil/e */
1154 if (lua_isnil(L
, -rv
)){
1155 lua_pop(L
, rv
); /* ud, tail, head, (label) */
1156 /* Edge not yet registered */
1157 edge
= lua_newuserdata(L
, sizeof(gr_edge_t
)); /* ud, peer, name, edge */
1159 sprintf(sbuf
, "edge@%lu", (unsigned long) AGID(e
));
1160 edge
->name
= strdup(sbuf
);
1161 edge
->type
= AGEDGE
;
1162 edge
->status
= ALIVE
;
1163 set_object(L
, e
); /* ud, peer, name, edge */
1165 lua_pushlightuserdata(L
, tail
);
1166 lua_pushlightuserdata(L
, head
);
1169 /* Edge already registered */
1170 lua_pushlightuserdata(L
, tail
);
1171 lua_pushlightuserdata(L
, head
);
1172 return rv
+ 2; /* ud, peer, name, edge */
1175 /* Edge already registered */
1176 lua_pushlightuserdata(L
, tail
);
1177 lua_pushlightuserdata(L
, head
);
1178 return rv
+ 2; /* ud, peer, name, edge */
1182 lua_pushstring(L
, "edge not found");
1186 /*-------------------------------------------------------------------------*\
1187 * Method: n, err = g.idnode(self, id)
1188 * Finds a node by id.
1191 * n, err = g:idnode(18)
1192 \*-------------------------------------------------------------------------*/
1193 static int gr_idnode(lua_State
*L
)
1196 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
1197 unsigned long id
= (unsigned long) luaL_checknumber(L
, 2);
1198 if ((n
= agidnode(ud
->g
, id
, 0)) != NULL
){
1199 return get_object(L
, n
);
1202 lua_pushstring(L
, "agidnode failed");
1207 /*-------------------------------------------------------------------------*\
1208 * Method: n = nextnode(self, prev)
1209 * Retrieves next node in a graph.
1210 * Returns the successor of the given node. With nil as parameter prev the
1211 * first node is returned.
1213 * first = g:nextnode(nil)
1214 * second = g:nextnode(first)
1215 * third = g:nextnode(second)
1216 \*-------------------------------------------------------------------------*/
1217 static int gr_nextnode(lua_State
*L
)
1222 gr_graph_t
*ud_g
= tograph(L
, 1, STRICT
);
1223 if (lua_isnil(L
, 2))
1224 n
= agfstnode(ud_g
->g
);
1226 ud_n
= tonode(L
, 2, STRICT
);
1227 n
= agnxtnode(ud_g
->g
, ud_n
->n
);
1234 /* Check whether userdata exists .. */
1235 rv
= get_object(L
, n
);
1237 /* .. yes: return it */
1240 /* .. no: create it */
1242 ud_n
= lua_newuserdata(L
, sizeof(gr_node_t
));
1244 ud_n
->name
= strdup(agnameof(n
));
1245 ud_n
->type
= AGNODE
;
1246 ud_n
->status
= ALIVE
;
1253 /*-------------------------------------------------------------------------*\
1254 * Iterator: walknodes()
1255 * Iterator over all nodes of a given graph.
1257 * for n in g:walknodes() do ... end
1258 \*-------------------------------------------------------------------------*/
1259 static int gr_walknodes(lua_State
*L
)
1261 lua_pushcfunction(L
, gr_nextnode
); /* ud, nextnode */
1262 lua_pushvalue(L
, -2); /* ud, nextnode, g */
1263 lua_pushnil(L
); /* ud, nextnode, g, nil */
1267 /*-------------------------------------------------------------------------*\
1268 * Property: g.graph [userdata]
1269 * Retrieves the graph to which given subgraph belongs. Trivial because it
1271 * Returns graph userdata.
1274 \*-------------------------------------------------------------------------*/
1275 int gr_graphof(lua_State
*L
)
1277 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
1278 Agraph_t
*g
= agraphof(ud
->g
);
1281 lua_pushstring(L
, "no graph");
1284 return get_object(L
, ud
->g
);
1287 /*-------------------------------------------------------------------------*\
1288 * Method: rv = g.contains(self, obj)
1289 * Returns true if graph g contains object obj.
1292 \*-------------------------------------------------------------------------*/
1293 int gr_contains(lua_State
*L
)
1295 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
1296 gr_object_t
*obj
= toobject(L
, 2, NULL
, STRICT
);
1297 int rv
= agcontains(ud
->g
, obj
->p
.p
);
1298 if (rv
&& AGTYPE(obj
->p
.p
) == AGNODE
){
1299 if (agroot(obj
->n
.n
) == agroot(ud
->g
))
1300 lua_pushboolean(L
, TRUE
);
1302 lua_pushboolean(L
, FALSE
);
1304 lua_pushboolean(L
, rv
);
1308 /*-------------------------------------------------------------------------*\
1309 * Method: rv = g.layout(self, fmt)
1310 * Layout the given graph in the specified format/algorithm.
1312 * b = g:layout("dot")
1313 \*-------------------------------------------------------------------------*/
1314 static int gr_layout(lua_State
*L
)
1317 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
1318 char *fmt
= (char *) luaL_optstring(L
, 2, "dot");
1320 if (!strcmp(fmt
, "dot") ||
1321 !strcmp(fmt
, "neato") ||
1322 !strcmp(fmt
, "nop") ||
1323 !strcmp(fmt
, "nop2") ||
1324 !strcmp(fmt
, "twopi") ||
1325 !strcmp(fmt
, "fdp") ||
1326 !strcmp(fmt
, "circo")){
1328 if ((rv
= gv_layout(ud
->g
, fmt
)) != GR_SUCCESS
){
1329 luaL_error(L
, "layout error: %d", rv
);
1332 lua_pushvalue(L
, rv
);
1335 luaL_error(L
, "invalid layout format '%s'", fmt
);
1340 /*-------------------------------------------------------------------------*\
1341 * Method: rv = g.freelayout(self, fmt)
1344 * b = g:freelayout()
1345 \*-------------------------------------------------------------------------*/
1346 static int gr_freelayout(lua_State
*L
)
1348 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
1350 gv_free_layout(ud
->g
);
1353 lua_pushstring(L
, "invalid graph");
1356 lua_pushnumber(L
, 0);
1359 /*-------------------------------------------------------------------------*\
1360 * Method: rv = g.render(self, rfmt, file, lfmt)
1361 * Render the given graph in the specified format.
1363 * b = g:render("pdf")
1364 \*-------------------------------------------------------------------------*/
1365 static int gr_render(lua_State
*L
)
1368 gr_graph_t
*ud
= tograph(L
, 1, STRICT
);
1369 char *rfmt
= (char *) luaL_optstring(L
, 2, "plain");
1370 char *fname
= (char *) luaL_optstring(L
, 3, NULL
);
1371 char *lfmt
= (char *) luaL_optstring(L
, 4, NULL
);
1374 lua_pushstring(L
, "layout missing");
1378 gv_layout(ud
->g
, lfmt
);
1380 rv
= gv_render_file(ud
->g
, rfmt
, fname
);
1382 rv
= gv_render(ud
->g
, rfmt
, stdout
);
1384 gv_free_layout(ud
->g
);
1385 if (rv
!= GR_SUCCESS
){
1387 lua_pushstring(L
, "gvRender failed");
1390 lua_pushnumber(L
, rv
);
1394 /*-------------------------------------------------------------------------*\
1395 * Method: list, count = graph.plugins(type)
1396 * Retrieve available plugins for layout or rendering.
1397 * type: layout | render
1399 * tab, err = g:layout("layout")
1400 \*-------------------------------------------------------------------------*/
1401 static int gr_plugins(lua_State
*L
)
1406 char *kind
= (char *) luaL_optstring(L
, 1, "render");
1408 list
= gvPluginList(gvc
, kind
, &count
, NULL
);
1410 list
= gvPluginList(gvc
, kind
, &count
);
1414 lua_pushstring(L
, "no plugins");
1418 lua_newtable(L
); /* t */
1419 for (i
= 0; i
< count
; i
++){
1420 lua_pushnumber(L
, i
+1); /* t, index */
1421 lua_pushstring(L
, list
[i
]); /* t, index, value */
1422 lua_settable(L
, -3); /* t */
1424 lua_pushnumber(L
, count
);
1425 for (i
= 0; i
< count
; i
++)
1430 /*-------------------------------------------------------------------------*\
1431 * Module initialization
1432 \*-------------------------------------------------------------------------*/
1433 LUALIB_API
int luaopen_graph_core(lua_State
*L
) {
1435 lua_pushvalue(L
, -1);
1436 lua_setglobal(L
, MYNAME
);
1437 register_metainfo(L
, funcs
);
1438 lua_pushliteral(L
, "version");
1439 lua_pushliteral(L
, MYVERSION
);
1441 lua_pushliteral(L
, "_VERSION");
1442 lua_pushliteral(L
, MYVERSION
);
1444 lua_pushliteral(L
, "_GVVERSION");
1445 lua_pushliteral(L
, MYGVVERSION
);
1447 lua_pushliteral(L
, "_COPYRIGHT");
1448 lua_pushliteral(L
, MYCOPYRIGHT
);
1450 lua_pushliteral(L
, "_DESCRIPTION");
1451 lua_pushliteral(L
, MYDESCRIPTION
);
1453 lua_pushliteral(L
, "_SYSTEM");
1454 lua_pushliteral(L
, MYSYSTEM
);
1456 lua_pushliteral(L
, "plugins");
1457 lua_pushcfunction(L
, gr_plugins
);
1459 if ((gvc
= gvContextPlugins(lt_preloaded_symbols
, DEMAND_LOADING
)) == NULL
){
1460 return luaL_error(L
, "cannot load plugins");
1465 /*-------------------------------------------------------------------------*\
1466 * Metamethod: __tostring [boolean]
1467 * Return a string presentation of a graph object.
1469 * g == h or g != h with metamethods
1470 \*-------------------------------------------------------------------------*/
1471 static int gr_tostring(lua_State
*L
)
1473 gr_object_t
*ud
= toobject(L
, 1, NULL
, NONSTRICT
);
1474 lua_pushfstring(L
, "graph: %p (%s)", ud
, ud
->p
.status
== ALIVE
? "alive" : "dead");