update index.html documentation
[luagraph.git] / src / gr_graph.c
blob11be48d2da1454e36a3567ba862c4490f59685dc
1 /*=======================rap==================================================*\
2 * LuaGRAPH toolkit
3 * Graph support for Lua.
4 * Herbert Leuwer
5 * 30-7-2006, 30-12-2016, 01/2017
7 * Graph related functionality.
9 \*=========================================================================*/
11 /*=========================================================================*\
12 * Includes
13 \*=========================================================================*/
14 #include <string.h>
15 #include <stdlib.h>
17 #include "lua.h"
18 #include "lauxlib.h"
20 #include "gr_graph.h"
21 #include "graphviz/cdt.h"
22 #include "graphviz/gvplugin.h"
24 /*=========================================================================*\
25 * Defines
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"
32 #else
33 #define MYSYSTEM "other"
34 #define MYGVVERSION GVVERSION
35 #endif
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 /*=========================================================================*\
41 * Prototypes
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 /*=========================================================================*\
85 * Data
86 \*=========================================================================*/
87 GVC_t *gvc = NULL;
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;
94 #if 0
95 extern gvplugin_library_t gvplugin_webp_LTX_library;
96 #endif
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) },
103 #if 0
104 { "gvplugin_webp_LTX_library", (void*)(&gvplugin_webp_LTX_library) },
105 #endif
106 { "gvplugin_gd_LTX_library", (void*)(&gvplugin_gd_LTX_library) },
107 { "gvplugin_core_LTX_library", (void*)(&gvplugin_core_LTX_library) },
108 { 0, 0 }
110 #else
111 lt_symlist_t lt_preloaded_symbols[] = {
112 { 0, 0 }
114 #endif
116 * Base library functions
118 static const luaL_Reg funcs[] = {
119 {"open", gr_open},
120 {"close", gr_close},
121 {"read", gr_read},
122 {"memread", gr_memread},
123 {"equal", gr_equal},
124 {NULL, NULL}
128 * Graph object methods
130 static const luaL_Reg reg_methods[] = {
131 {"close", gr_close},
132 {"write", gr_write},
133 {"subgraph", gr_subgraph},
134 {"getnext", gr_getnext},
135 {"nextgraph", gr_getnext},
136 {"walk", gr_walk},
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},
149 {"node", gr_node},
150 {"edge", gr_edge},
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},
160 {"rawget", getval},
161 {NULL, NULL}
165 * Read-only members
167 static const luaL_Reg reg_rmembers[] = {
168 {"nnodes", gr_nnodes},
169 {"nedges", gr_nedges},
170 {"name", gr_nameof},
171 {"status", gr_status},
172 {"root", gr_root},
173 {"parent", gr_parent},
174 {"isstrict", gr_isstrict},
175 {"isdirected", gr_isdirected},
176 {"isroot", gr_isroot},
177 {"graph", gr_graphof},
178 {"id", gr_id},
179 {NULL, NULL}
183 * Standard metamethods
185 static const luaL_Reg reg_metamethods[] = {
186 {"__gc", gr_collect},
187 {"__eq", gr_equal},
188 {"__newindex", object_newindex_handler},
189 {"__tostring", gr_tostring},
190 {NULL, NULL}
194 * Event callback functions - used for registring/unregstring proxy objects
195 * in Lua registry.
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 /*=========================================================================*\
204 * Functions
205 \*=========================================================================*/
206 /*-------------------------------------------------------------------------*\
207 * Utility Functions
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);
226 if (rv != 0)
227 return GR_ERROR;
228 return GR_SUCCESS;
231 static int gv_free_layout(Agraph_t *g)
233 int rv = gvFreeLayout(gvc, g);
234 if (rv != 0)
235 return GR_ERROR;
236 return GR_SUCCESS;
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);
246 if (rv != 0)
247 return GR_ERROR;
248 return GR_SUCCESS;
251 * Render layouted graph into a file given by file name using given
252 * format.
254 static int gv_render_file(Agraph_t *g, const char *fmt, const char *fname)
256 int rv = gvRenderFilename(gvc, g, fmt, fname);
257 if (rv != 0)
258 return GR_ERROR;
259 return GR_SUCCESS;
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);
271 Agsym_t *sym = NULL;
273 lua_newtable(L); /* rt */
274 switch (kind){
275 case AGRAPH:
276 case AGNODE:
277 case AGEDGE:
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 */
283 return 1;
284 break;
285 default:
286 return luaL_error(L, "invalid kind");
287 break;
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)
298 int n = 0;
299 char *key;
300 char *value;
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");
312 return 0;
314 key = (char *) lua_tostring(L, -2);
315 value = (char *) lua_tostring(L, -1);
316 if (value == NULL){
317 lua_pop(L,2);
318 luaL_error(L, "invalid value");
319 return 0;
321 lua_pop(L, 1); /* t, key */
322 n++;
323 agattr(ud->g, kind, key, value);
325 lua_pushnumber(L, n);
326 return 1;
329 /*-------------------------------------------------------------------------* \
330 * Function: g, err = graph.open(name [,kind])
331 * Create a new graph
332 * Returns graph userdata.
333 * Example:
334 * g, err = graph.open(name[, kind])
335 \*-------------------------------------------------------------------------*/
336 static int gr_open(lua_State *L)
338 gr_graph_t *ud;
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")) {
344 kind = Agdirected;
345 } else if (!strcmp(skind, "strictdirected")){
346 kind = Agstrictdirected;
347 } else if (!strcmp(skind, "undirected")) {
348 kind = Agundirected;
349 } else if (!strcmp(skind, "strictundirected")) {
350 kind = Agstrictundirected;
351 } else {
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))){
358 lua_pushnil(L);
359 lua_pushstring(L, "open failed");
360 return 2;
362 ud->name = strdup(name);
363 ud->type = AGRAPH;
364 ud->status = ALIVE;
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");
371 return 0;
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);
380 return new_graph(L);
383 /*-------------------------------------------------------------------------*\
384 * Method: g.close(self)
385 * Close a graph
386 * Returns always 0.
387 * Example:
388 * rv = graph.close(g)
389 * rv = g:close()
390 \*-------------------------------------------------------------------------*/
391 static int gr_close(lua_State *L)
393 gr_graph_t *ud = tograph(L, 1, STRICT);
395 if (ud->g != NULL){
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__);
401 agclose(ud->g);
403 } else {
404 TRACE(" g:close(): graph: ud=%p already closed (%s %d)\n", (void *) ud, __FILE__, __LINE__);
406 lua_pushnumber(L, 0);
407 return 1;
411 /*-------------------------------------------------------------------------*\
412 * Property: name [string]
413 * Returns the name of a graph.
414 * Example:
415 * name = g.name
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");
422 return 0;
424 lua_pushstring(L, agnameof(ud->g));
425 return 1;
428 /*-------------------------------------------------------------------------*\
429 * Property: isstrict [boolean]
430 * Returns true is graph is strict, otherwise it returns false.
431 * Example:
432 * rv = g.isstrict
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));
438 return 1;
441 /*-------------------------------------------------------------------------*\
442 * Property: isdirected [boolean]
443 * Returns true if the graph is directed, otherwise it returns false
444 * Example:
445 * rv = g.isdirected
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));
451 return 1;
454 /*-------------------------------------------------------------------------* \
455 * Read a graph from a string
456 * Returns graph userdata.
457 * Example:
458 * g, err = graph.memread(str)
459 \*-------------------------------------------------------------------------*/
460 static int gr_memread(lua_State *L)
462 gr_graph_t *ud;
463 char *str = (char *)lua_tostring(L, 1);
465 ud = lua_newuserdata(L, sizeof(gr_graph_t));
466 if (!(ud->g = agmemread(str))){
467 lua_pushnil(L);
468 lua_pushstring(L, "agread failed");
469 return 2;
471 ud->name = strdup(agnameof(ud->g));
472 ud->type = AGRAPH;
473 ud->status = ALIVE;
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);
480 return new_graph(L);
483 /*-------------------------------------------------------------------------* \
484 * Read a graph from a file; "stdin" reads from STDIN.
485 * Returns graph userdata.
486 * Example:
487 * g, err = graph.read(filename)
488 \*-------------------------------------------------------------------------*/
489 static int gr_read(lua_State *L)
491 gr_graph_t *ud;
492 FILE *fin;
493 char *fname = (char *)luaL_optstring(L, 1, "stdin");
495 if (!strcmp(fname, "stdin")){
496 fin = stdin;
497 } else {
498 fin = fopen(fname, "r");
500 if (!fin){
501 lua_pushnil(L);
502 lua_pushstring(L, "fopen failed");
503 return 2;
505 /* Create a userdata */
506 ud = lua_newuserdata(L, sizeof(gr_graph_t));
507 if (!(ud->g = agread(fin, NIL(Agdisc_t*)))){
508 fclose(fin);
509 lua_pushnil(L);
510 lua_pushstring(L, "agread failed");
511 return 2;
513 fclose(fin);
514 ud->name = strdup(agnameof(ud->g));
515 ud->type = AGRAPH;
516 ud->status = ALIVE;
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);
523 return new_graph(L);
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)
534 int rv;
535 FILE *fout;
536 int stdflag = 0;
537 gr_graph_t *ud = tograph(L, 1, STRICT);
538 char *fname = (char*) luaL_optstring(L, 2, "__std__");
540 if (!strcmp(fname, "__std__")){
541 stdflag = 1;
542 fout = stdout;
543 } else {
544 fout = fopen(fname, "w+");
546 if (!fout){
547 lua_pushnil(L);
548 lua_pushstring(L, "fopen failed");
549 return 2;
551 rv = agwrite(ud->g, fout);
552 if (rv){
553 if (!stdflag)
554 fclose(fout);
555 lua_pushnil(L);
556 lua_pushstring(L, "agwrite failed");
557 return 2;
559 if (!stdflag)
560 fclose(fout);
561 lua_pushnumber(L, rv);
562 return 1;
565 /*-------------------------------------------------------------------------*\
566 * Property:nnodes [number]
567 * Provides the numebr of edges of a graph
568 * Example:
569 * rv = g.nnodes
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));
575 return 1;
578 /*-------------------------------------------------------------------------*\
579 * Property: nedges [number]
580 * Provides the number of edges of a graph.
581 * Example:
582 * rv = g.nedges
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));
588 return 1;
591 /*-------------------------------------------------------------------------*\
592 * Metamethod: __eq [boolean]
593 * Compare two graphs
594 * Example:
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));
602 return 1;
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.
609 * Example:
610 * sg, err = g:subgraph(name)
611 \*-------------------------------------------------------------------------*/
612 static int gr_subgraph(lua_State *L)
614 int rv;
615 Agraph_t *g;
616 gr_graph_t *sg;
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));
627 sg->g = g;
628 sg->name = strdup(name);
629 sg->type = AGRAPH;
630 sg->status = ALIVE;
631 return new_graph(L);
632 } else
633 return rv;
634 } else {
635 /* No - Create a new one */
636 if (lua_toboolean(L, 3)){
637 lua_pushnil(L);
638 lua_pushstring(L, "graph not found");
639 return 2;
641 sg = lua_newuserdata(L, sizeof(gr_graph_t));
642 if (!(sg->g = agsubg(ud->g, name, 1))){
643 lua_pushnil(L);
644 lua_pushstring(L, "agsubg failed");
645 return 2;
647 if (name)
648 sg->name = strdup(name);
649 sg->type = AGRAPH;
650 sg->status = ALIVE;
651 return new_graph(L);
655 /*-------------------------------------------------------------------------*\
656 * Property: root [userdata]
657 * Get root of graph.
658 * Example:
659 * sg, err = g.root
660 \*-------------------------------------------------------------------------*/
661 static int gr_root(lua_State *L)
663 gr_graph_t *ud = tograph(L, 1, STRICT);
664 Agraph_t *g;
666 if (!(g = agroot(ud->g))){
667 lua_pushnil(L);
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.
677 * Example:
678 * sg, err = g.parent
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.
693 * Example:
694 * first = g:getnext(nil)
695 * second = g:getnext(first)
696 * third = g:getnext(second)
697 \*-------------------------------------------------------------------------*/
698 static int gr_getnext(lua_State *L)
700 int rv;
701 Agraph_t *g;
702 gr_graph_t *ud_sg;
703 gr_graph_t *ud = tograph(L, 1, STRICT);
704 if (lua_isnil(L, 2)){
705 g = agfstsubg(ud->g);
706 } else {
707 ud_sg = tograph(L, 2, STRICT);
708 g = agnxtsubg(ud_sg->g);
710 if (!g){
711 lua_pushnil(L);
712 return 1;
713 } else {
714 rv = get_object(L, g);
715 if (rv == 1)
716 return rv;
717 else {
718 lua_pop(L, rv);
719 ud_sg = lua_newuserdata(L, sizeof(gr_graph_t));
720 ud_sg->g = g;
721 ud_sg->name = strdup(agnameof(g));
722 ud_sg->type = AGRAPH;
723 ud_sg->status = ALIVE;
724 set_object(L, g);
725 return new_graph(L);
730 /*-------------------------------------------------------------------------*\
731 * Iterator: g.walk(self)
732 * Iterator over all subgraphs of the given graph.
733 * Example:
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*/
741 return 3;
744 /*-------------------------------------------------------------------------*\
745 * Property: id [number]
746 * Get a graph's id.
747 * Example:
748 * n = g.id
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));
754 return 1;
757 /*-------------------------------------------------------------------------*\
758 * Property: isroot [boolean]
759 * Is given graph a root ?
760 * Example:
761 * b = g.isroot
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);
767 return 1;
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.
775 * Example:
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}}
797 * Example:
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 */
816 return 1;
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.
825 * Example:
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.
848 * Example:
849 * rv, err = g:setattr{graph={...}, node={shape="box"}, edge={...}}
850 \*-------------------------------------------------------------------------*/
851 static int gr_setattr(lua_State *L)
853 int n = 0;
854 int rv;
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 */
864 } else
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 */
875 } else
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 */
886 } else
887 lua_pop(L, 1);
889 lua_pushnumber(L, n); /* ud, t, n */
890 return 1;
893 /*-------------------------------------------------------------------------*\
894 * Method: g.delete(self, obj)
895 * Deletes an object from the given graph.
896 * Returns non-nil.
897 * Example:
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)){
907 case AGRAPH:
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 */
914 break;
915 case AGNODE:
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 */
922 break;
923 case AGEDGE:
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 */
930 break;
932 lua_pushnumber(L, 0);
933 return 1;
937 * A simple wrapper for usage of gr_node in other files.
939 int gr_create_node(lua_State *L)
941 return gr_node(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.
948 * Returns userdata.
949 * Examples:
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.
954 * n, err = g:node()
955 * -
956 \*-------------------------------------------------------------------------*/
957 static int gr_node(lua_State *L)
959 Agnode_t *n;
960 gr_node_t *node;
961 int rv;
962 char *name;
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 */
975 node->n = n;
976 node->name = strdup(agnameof(n));
977 node->type = AGNODE;
978 node->status = ALIVE;
979 return new_node(L);
980 } else
981 /* Node already registered */
982 return rv; /* ud, name, node */
983 } else {
984 /* Node does not exist */
985 if (lua_toboolean(L, 3)){
986 /* auto-creation forbidden: return an error */
987 lua_pushnil(L);
988 lua_pushstring(L, "node not found");
989 return 2;
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");
995 return 0;
997 node->name = strdup(name);
998 node->type = AGNODE;
999 node->status = ALIVE;
1000 return new_node(L);
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
1007 * is optional.
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.
1012 * Examples:
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)
1019 Agedge_t *e;
1020 gr_edge_t *edge;
1021 gr_node_t *tail, *head;
1022 int rv;
1023 char ename[32];
1024 char *label;
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);
1034 } else {
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) */
1047 tail_created = 1;
1049 /* param 3: head node */
1050 if (lua_isuserdata(L, 3))
1051 head = tonode(L, 3, STRICT);
1052 else {
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) */
1065 head_created = 1;
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__);
1074 /* Edge exists */
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))
1083 return rv;
1085 lua_pushlightuserdata(L, tail);
1086 lua_pushlightuserdata(L, head);
1087 return 3;
1089 /* Edge doees not exist */
1090 if (lua_isboolean(L, 5)){
1091 /* creation of edge forbidden: nocreate flag is set */
1092 if (tail_created)
1093 del_object(L, tail->n);
1094 if (head_created)
1095 del_object(L, head->n);
1096 lua_pushnil(L);
1097 lua_pushstring(L, "edge not found");
1098 return 2;
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");
1104 return 0;
1106 sprintf(ename, "edge@%u", newid());
1107 if (!(edge->e = agedge(ud->g, tail->n, head->n, ename, 1))){
1108 /* creation failed */
1109 if (tail_created)
1110 del_object(L, tail->n);
1111 if (head_created)
1112 del_object(L, head->n);
1113 lua_pushnil(L);
1114 lua_pushstring(L, "agedge failed");
1115 return 2;
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__);
1120 if (label)
1121 agsafeset(edge->e, "label", label, NULL);
1122 edge->name = strdup(ename);
1123 edge->type = AGEDGE;
1124 edge->status = ALIVE;
1125 new_edge(L);
1126 lua_pushlightuserdata(L, tail);
1127 lua_pushlightuserdata(L, head);
1128 return 3;
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.
1137 * Example:
1138 * e, tail, head = g:findedge(n1, "node@2", "edge@122")
1139 \*-------------------------------------------------------------------------*/
1140 static int gr_findedge(lua_State *L)
1142 Agedge_t *e;
1143 gr_edge_t *edge;
1144 int rv;
1145 char sbuf[32];
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 */
1158 edge->e = e;
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 */
1164 new_edge(L);
1165 lua_pushlightuserdata(L, tail);
1166 lua_pushlightuserdata(L, head);
1167 return 3;
1168 } else {
1169 /* Edge already registered */
1170 lua_pushlightuserdata(L, tail);
1171 lua_pushlightuserdata(L, head);
1172 return rv + 2; /* ud, peer, name, edge */
1174 } else {
1175 /* Edge already registered */
1176 lua_pushlightuserdata(L, tail);
1177 lua_pushlightuserdata(L, head);
1178 return rv + 2; /* ud, peer, name, edge */
1180 } else {
1181 lua_pushnil(L);
1182 lua_pushstring(L, "edge not found");
1183 return 2;
1186 /*-------------------------------------------------------------------------*\
1187 * Method: n, err = g.idnode(self, id)
1188 * Finds a node by id.
1189 * Returns userdata.
1190 * Example:
1191 * n, err = g:idnode(18)
1192 \*-------------------------------------------------------------------------*/
1193 static int gr_idnode(lua_State *L)
1195 Agnode_t *n;
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);
1200 } else {
1201 lua_pushnil(L);
1202 lua_pushstring(L, "agidnode failed");
1203 return 2;
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.
1212 * Example:
1213 * first = g:nextnode(nil)
1214 * second = g:nextnode(first)
1215 * third = g:nextnode(second)
1216 \*-------------------------------------------------------------------------*/
1217 static int gr_nextnode(lua_State *L)
1219 int rv;
1220 Agnode_t *n;
1221 gr_node_t *ud_n;
1222 gr_graph_t *ud_g = tograph(L, 1, STRICT);
1223 if (lua_isnil(L, 2))
1224 n = agfstnode(ud_g->g);
1225 else {
1226 ud_n = tonode(L, 2, STRICT);
1227 n = agnxtnode(ud_g->g, ud_n->n);
1229 if (!n){
1230 /* no more nodes */
1231 lua_pushnil(L);
1232 return 1;
1233 } else {
1234 /* Check whether userdata exists .. */
1235 rv = get_object(L, n);
1236 if (rv == 1)
1237 /* .. yes: return it */
1238 return rv;
1239 else {
1240 /* .. no: create it */
1241 lua_pop(L, rv);
1242 ud_n = lua_newuserdata(L, sizeof(gr_node_t));
1243 ud_n->n = n;
1244 ud_n->name = strdup(agnameof(n));
1245 ud_n->type = AGNODE;
1246 ud_n->status = ALIVE;
1247 set_object(L, n);
1248 return new_node(L);
1253 /*-------------------------------------------------------------------------*\
1254 * Iterator: walknodes()
1255 * Iterator over all nodes of a given graph.
1256 * Example:
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 */
1264 return 3;
1267 /*-------------------------------------------------------------------------*\
1268 * Property: g.graph [userdata]
1269 * Retrieves the graph to which given subgraph belongs. Trivial because it
1270 * returns itself.
1271 * Returns graph userdata.
1272 * Example:
1273 * g, err = g.graph
1274 \*-------------------------------------------------------------------------*/
1275 int gr_graphof(lua_State *L)
1277 gr_graph_t *ud = tograph(L, 1, STRICT);
1278 Agraph_t *g = agraphof(ud->g);
1279 if (g == NULL){
1280 lua_pushnil(L);
1281 lua_pushstring(L, "no graph");
1282 return 2;
1284 return get_object(L, ud->g);
1287 /*-------------------------------------------------------------------------*\
1288 * Method: rv = g.contains(self, obj)
1289 * Returns true if graph g contains object obj.
1290 * Example:
1291 * b = g:contains(n)
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);
1301 else
1302 lua_pushboolean(L, FALSE);
1303 } else
1304 lua_pushboolean(L, rv);
1305 return 1;
1308 /*-------------------------------------------------------------------------*\
1309 * Method: rv = g.layout(self, fmt)
1310 * Layout the given graph in the specified format/algorithm.
1311 * Example:
1312 * b = g:layout("dot")
1313 \*-------------------------------------------------------------------------*/
1314 static int gr_layout(lua_State *L)
1316 int rv;
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);
1330 return 0;
1332 lua_pushvalue(L, rv);
1333 return 1;
1334 } else {
1335 luaL_error(L, "invalid layout format '%s'", fmt);
1336 return 0;
1340 /*-------------------------------------------------------------------------*\
1341 * Method: rv = g.freelayout(self, fmt)
1342 * Free a layout
1343 * Example:
1344 * b = g:freelayout()
1345 \*-------------------------------------------------------------------------*/
1346 static int gr_freelayout(lua_State *L)
1348 gr_graph_t *ud = tograph(L, 1, STRICT);
1349 if (ud->g)
1350 gv_free_layout(ud->g);
1351 else {
1352 lua_pushnil(L);
1353 lua_pushstring(L, "invalid graph");
1354 return 2;
1356 lua_pushnumber(L, 0);
1357 return 1;
1359 /*-------------------------------------------------------------------------*\
1360 * Method: rv = g.render(self, rfmt, file, lfmt)
1361 * Render the given graph in the specified format.
1362 * Example:
1363 * b = g:render("pdf")
1364 \*-------------------------------------------------------------------------*/
1365 static int gr_render(lua_State *L)
1367 int rv;
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);
1372 if (gvc == NULL){
1373 lua_pushnil(L);
1374 lua_pushstring(L, "layout missing");
1375 return 2;
1377 if (lfmt)
1378 gv_layout(ud->g, lfmt);
1379 if (fname)
1380 rv = gv_render_file(ud->g, rfmt, fname);
1381 else
1382 rv = gv_render(ud->g, rfmt, stdout);
1383 if (lfmt)
1384 gv_free_layout(ud->g);
1385 if (rv != GR_SUCCESS){
1386 lua_pushnil(L);
1387 lua_pushstring(L, "gvRender failed");
1388 return 2;
1390 lua_pushnumber(L, rv);
1391 return 1;
1394 /*-------------------------------------------------------------------------*\
1395 * Method: list, count = graph.plugins(type)
1396 * Retrieve available plugins for layout or rendering.
1397 * type: layout | render
1398 * Example:
1399 * tab, err = g:layout("layout")
1400 \*-------------------------------------------------------------------------*/
1401 static int gr_plugins(lua_State *L)
1403 int i, count;
1404 char **list;
1406 char *kind = (char *) luaL_optstring(L, 1, "render");
1407 #if 0
1408 list = gvPluginList(gvc, kind, &count, NULL);
1409 #else
1410 list = gvPluginList(gvc, kind, &count);
1411 #endif
1412 if (list == NULL){
1413 lua_pushnil(L);
1414 lua_pushstring(L, "no plugins");
1415 return 2;
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++)
1426 free(list[i]);
1427 free(list);
1428 return 2;
1430 /*-------------------------------------------------------------------------*\
1431 * Module initialization
1432 \*-------------------------------------------------------------------------*/
1433 LUALIB_API int luaopen_graph_core(lua_State *L) {
1434 lua_newtable(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);
1440 lua_rawset(L, -3);
1441 lua_pushliteral(L, "_VERSION");
1442 lua_pushliteral(L, MYVERSION);
1443 lua_rawset(L, -3);
1444 lua_pushliteral(L, "_GVVERSION");
1445 lua_pushliteral(L, MYGVVERSION);
1446 lua_rawset(L, -3);
1447 lua_pushliteral(L, "_COPYRIGHT");
1448 lua_pushliteral(L, MYCOPYRIGHT);
1449 lua_rawset(L, -3);
1450 lua_pushliteral(L, "_DESCRIPTION");
1451 lua_pushliteral(L, MYDESCRIPTION);
1452 lua_rawset(L, -3);
1453 lua_pushliteral(L, "_SYSTEM");
1454 lua_pushliteral(L, MYSYSTEM);
1455 lua_rawset(L, -3);
1456 lua_pushliteral(L, "plugins");
1457 lua_pushcfunction(L, gr_plugins);
1458 lua_rawset(L, -3);
1459 if ((gvc = gvContextPlugins(lt_preloaded_symbols, DEMAND_LOADING)) == NULL){
1460 return luaL_error(L, "cannot load plugins");
1462 return 1;
1465 /*-------------------------------------------------------------------------*\
1466 * Metamethod: __tostring [boolean]
1467 * Return a string presentation of a graph object.
1468 * Example:
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");
1475 return 1;