Create CODE_OF_CONDUCT.md
[luagraph.git] / src / gr_graph.c
blob51a985662f9e7dcacf20f8ab33f09d1b73d9cae0
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.0.0"
29 #if defined(_WIN32) || defined(WIN32)
30 #define MYGVVERSION "2.38"
31 #define MYSYSTEM "Win32"
32 #else
33 #define MYSYSTEM "other"
34 #define MYGVVERSION GVVERSION
35 #endif
36 #define MYCOPYRIGHT "Copyright (C) 2006-2017, Herbert Leuwer "
37 #define MYDESCRIPTION "LuaGRAPH is a library for creating, manipulating and rendering GRAPHs (based on the GRAPHVIZ library cgraph)."
39 /*=========================================================================*\
40 * Prototypes
41 \*=========================================================================*/
42 static int gr_open(lua_State *L);
43 static int gr_close(lua_State *L);
44 static int gr_nameof(lua_State *L);
45 static int gr_write(lua_State *L);
46 static int gr_read(lua_State *L);
47 static int gr_memread(lua_State *L);
48 static int gr_isstrict(lua_State *L);
49 static int gr_isdirected(lua_State *L);
50 static int gr_nnodes(lua_State *L);
51 static int gr_nedges(lua_State *L);
52 static int gr_subgraph(lua_State *L);
53 static int gr_root(lua_State *L);
54 static int gr_parent(lua_State *L);
55 static int gr_equal(lua_State *L);
56 static int gr_getnext(lua_State *L);
57 static int gr_walk(lua_State *L);
58 static int gr_id(lua_State *L);
59 static int gr_isroot(lua_State *L);
60 static int gr_getgraphattr(lua_State *L);
61 static int gr_getnodeattr(lua_State *L);
62 static int gr_getedgeattr(lua_State *L);
63 static int gr_getattr(lua_State *L);
64 static int gr_setgraphattr(lua_State *L);
65 static int gr_setnodeattr(lua_State *L);
66 static int gr_setedgeattr(lua_State *L);
67 static int gr_setattr(lua_State *L);
68 static int gr_delete(lua_State *L);
69 static int gr_node(lua_State *L);
70 static int gr_edge(lua_State *L);
71 static int gr_findedge(lua_State *L);
72 static int gr_idnode(lua_State *L);
73 static int gr_nextnode(lua_State *L);
74 static int gr_walknodes(lua_State *L);
75 static int gr_graphof(lua_State *L);
76 static int gr_contains(lua_State *L);
77 static int gr_layout(lua_State *L);
78 static int gr_freelayout(lua_State *L);
79 static int gr_render(lua_State *L);
80 static int gr_plugins(lua_State *L);
81 static int gr_tostring(lua_State *L);
83 /*=========================================================================*\
84 * Data
85 \*=========================================================================*/
86 GVC_t *gvc = NULL;
88 #ifdef USE_BUILTIN_PLUGINS
89 extern gvplugin_library_t gvplugin_dot_layout_LTX_library;
90 extern gvplugin_library_t gvplugin_neato_layout_LTX_library;
91 extern gvplugin_library_t gvplugin_gd_LTX_library;
92 extern gvplugin_library_t gvplugin_pango_LTX_library;
93 #if 0
94 extern gvplugin_library_t gvplugin_webp_LTX_library;
95 #endif
96 extern gvplugin_library_t gvplugin_core_LTX_library;
98 lt_symlist_t lt_preloaded_symbols[] = {
99 { "gvplugin_dot_layout_LTX_library", (void*)(&gvplugin_dot_layout_LTX_library) },
100 { "gvplugin_neato_layout_LTX_library", (void*)(&gvplugin_neato_layout_LTX_library) },
101 { "gvplugin_pango_LTX_library", (void*)(&gvplugin_pango_LTX_library) },
102 #if 0
103 { "gvplugin_webp_LTX_library", (void*)(&gvplugin_webp_LTX_library) },
104 #endif
105 { "gvplugin_gd_LTX_library", (void*)(&gvplugin_gd_LTX_library) },
106 { "gvplugin_core_LTX_library", (void*)(&gvplugin_core_LTX_library) },
107 { 0, 0 }
109 #else
110 lt_symlist_t lt_preloaded_symbols[] = {
111 { 0, 0 }
113 #endif
115 * Base library functions
117 static const luaL_Reg funcs[] = {
118 {"open", gr_open},
119 {"close", gr_close},
120 {"read", gr_read},
121 {"memread", gr_memread},
122 {"equal", gr_equal},
123 {NULL, NULL}
127 * Graph object methods
129 static const luaL_Reg reg_methods[] = {
130 {"close", gr_close},
131 {"write", gr_write},
132 {"subgraph", gr_subgraph},
133 {"getnext", gr_getnext},
134 {"nextgraph", gr_getnext},
135 {"walk", gr_walk},
136 {"walkgraphs", gr_walk},
137 {"getgraphattr", gr_getgraphattr},
138 {"getnodeattr", gr_getnodeattr},
139 {"getedgeattr", gr_getedgeattr},
140 {"getattr", gr_getattr},
141 {"defaults", gr_getattr},
142 {"setgraphattr", gr_setgraphattr},
143 {"setnodeattr", gr_setnodeattr},
144 {"setedgeattr", gr_setedgeattr},
145 {"setattr", gr_setattr},
146 {"declare", gr_setattr},
147 {"delete", gr_delete},
148 {"node", gr_node},
149 {"edge", gr_edge},
150 {"findedge", gr_findedge},
151 {"idnode", gr_idnode},
152 {"nextnode", gr_nextnode},
153 {"walknodes", gr_walknodes},
154 {"type", get_object_type},
155 {"contains", gr_contains},
156 {"layout", gr_layout},
157 {"freelayout", gr_freelayout},
158 {"render", gr_render},
159 {"rawget", getval},
160 {NULL, NULL}
164 * Read-only members
166 static const luaL_Reg reg_rmembers[] = {
167 {"nnodes", gr_nnodes},
168 {"nedges", gr_nedges},
169 {"name", gr_nameof},
170 {"status", gr_status},
171 {"root", gr_root},
172 {"parent", gr_parent},
173 {"isstrict", gr_isstrict},
174 {"isdirected", gr_isdirected},
175 {"isroot", gr_isroot},
176 {"graph", gr_graphof},
177 {"id", gr_id},
178 {NULL, NULL}
182 * Standard metamethods
184 static const luaL_Reg reg_metamethods[] = {
185 {"__gc", gr_collect},
186 {"__eq", gr_equal},
187 {"__newindex", object_newindex_handler},
188 {"__tostring", gr_tostring},
189 {NULL, NULL}
193 * Event callback functions - used for registring/unregstring proxy objects
194 * in Lua registry.
196 static const struct Agcbdisc_s disc = {
197 { cb_insert, cb_modify, cb_delete}, /* graph callbacks */
198 { cb_insert, cb_modify, cb_delete}, /* node callbacks */
199 { cb_insert, cb_modify, cb_delete}, /* edge callbacks */
202 /*=========================================================================*\
203 * Functions
204 \*=========================================================================*/
205 /*-------------------------------------------------------------------------*\
206 * Utility Functions
207 \*-------------------------------------------------------------------------*/
210 * Create a new Lua userdata object and configure metatable.
212 static int new_graph(lua_State *L){
213 return new_object(L, "graph", reg_rmembers, reg_methods,
214 reg_metamethods, object_index_handler);
217 #define DEMAND_LOADING (1)
220 * Layout a graph using given engine.
222 static int gv_layout(Agraph_t *g, const char *engine)
224 int rv = gvLayout(gvc, g, engine);
225 if (rv != 0)
226 return GR_ERROR;
227 return GR_SUCCESS;
230 static int gv_free_layout(Agraph_t *g)
232 int rv = gvFreeLayout(gvc, g);
233 if (rv != 0)
234 return GR_ERROR;
235 return GR_SUCCESS;
239 * Render layouted graph into a file given by file handle
240 * using given format.
242 static int gv_render(Agraph_t *g, const char *fmt, FILE *fout)
244 int rv = gvRender(gvc, g, fmt, fout);
245 if (rv != 0)
246 return GR_ERROR;
247 return GR_SUCCESS;
250 * Render layouted graph into a file given by file name using given
251 * format.
253 static int gv_render_file(Agraph_t *g, const char *fmt, const char *fname)
255 int rv = gvRenderFilename(gvc, g, fmt, fname);
256 if (rv != 0)
257 return GR_ERROR;
258 return GR_SUCCESS;
262 * Gets attributes for a specific object type into a table rt.
263 * If no attributes are defined, the function returns an empty table.
264 * Lua entry stack: ud
265 * Lua exit stack: ud, rt
267 static int getattr(lua_State *L, int kind)
269 gr_graph_t *ud = tograph(L, 1, STRICT);
270 Agsym_t *sym = NULL;
272 lua_newtable(L); /* rt */
273 switch (kind){
274 case AGRAPH:
275 case AGNODE:
276 case AGEDGE:
277 while ((sym = agnxtattr(ud->g, kind, sym)) != NULL){
278 lua_pushstring(L, sym->name); /* rt, key */
279 lua_pushstring(L, sym->defval); /* rt, key, value */
280 lua_settable(L, -3); /* rt */
282 return 1;
283 break;
284 default:
285 return luaL_error(L, "invalid kind");
286 break;
291 * Sets attributes for a specific object type from a table.
292 * Lua entry stack: ud, t
293 * Lua exit stack: ud, t
295 static int setattr(lua_State *L, int kind)
297 int n = 0;
298 char *key;
299 char *value;
300 gr_graph_t *ud = tograph(L, 1, STRICT);
302 /* Parameter check */
303 luaL_checktype(L, -1, LUA_TTABLE);
305 /* traverse through all key/value pairs of given table */
306 lua_pushnil(L); /* t, nil */
307 while (lua_next(L, -2)){ /* t, key, val */
308 if (!lua_isstring(L, -2)){
309 lua_pop(L, 2); /* t */
310 luaL_error(L, "invalid key");
311 return 0;
313 key = (char *) lua_tostring(L, -2);
314 value = (char *) lua_tostring(L, -1);
315 if (value == NULL){
316 lua_pop(L,2);
317 luaL_error(L, "invalid value");
318 return 0;
320 lua_pop(L, 1); /* t, key */
321 n++;
322 agattr(ud->g, kind, key, value);
324 lua_pushnumber(L, n);
325 return 1;
328 /*-------------------------------------------------------------------------* \
329 * Function: g, err = graph.open(name [,kind])
330 * Create a new graph
331 * Returns graph userdata.
332 * Example:
333 * g, err = graph.open(name[, kind])
334 \*-------------------------------------------------------------------------*/
335 static int gr_open(lua_State *L)
337 gr_graph_t *ud;
338 Agdesc_t kind = Agdirected;
339 char *name = (char *) luaL_checkstring(L, 1);
340 char *skind = (char *) luaL_optstring(L, 2, "directed");
342 if (!strcmp(skind, "directed")) {
343 kind = Agdirected;
344 } else if (!strcmp(skind, "strictdirected")){
345 kind = Agstrictdirected;
346 } else if (!strcmp(skind, "undirected")) {
347 kind = Agundirected;
348 } else if (!strcmp(skind, "strictundirected")) {
349 kind = Agstrictundirected;
350 } else {
351 luaL_error(L, "invalid graph attribute");
354 /* Create a userdata */
355 ud = lua_newuserdata(L, sizeof(gr_graph_t)); /* ud */
356 if (!(ud->g = agopen(name, kind, &AgDefaultDisc))){
357 lua_pushnil(L);
358 lua_pushstring(L, "open failed");
359 return 2;
361 ud->name = strdup(name);
362 ud->type = AGRAPH;
363 ud->status = ALIVE;
364 /* We need a few default fields */
365 if (!agattr(ud->g, AGEDGE, "label", "") ||
366 !agattr(ud->g, AGRAPH, "__attrib__", "") ||
367 !agattr(ud->g, AGEDGE, "__attrib__", "") ||
368 !agattr(ud->g, AGNODE, "__attrib__", "")){
369 luaL_error(L, "declaration failed");
370 return 0;
373 /* We set callbacks only in root graph */
374 if (ud->g == agroot(ud->g)){
375 agpushdisc(ud->g, (struct Agcbdisc_s *)&disc, L);
378 set_object(L, ud->g);
379 return new_graph(L);
382 /*-------------------------------------------------------------------------*\
383 * Method: g.close(self)
384 * Close a graph
385 * Returns always 0.
386 * Example:
387 * rv = graph.close(g)
388 * rv = g:close()
389 \*-------------------------------------------------------------------------*/
390 static int gr_close(lua_State *L)
392 gr_graph_t *ud = tograph(L, 1, STRICT);
394 if (ud->g != NULL){
396 if (ud->status == ALIVE) {
397 /* Delete the graph, if it still exists */
398 TRACE(" g:close(): graph: ud=%p '%s' ptr=%p type=%d (%s %d)\n",
399 (void *) ud, agnameof(ud->g), (void *)ud->g, AGTYPE(ud->g), __FILE__, __LINE__);
400 agclose(ud->g);
402 } else {
403 TRACE(" g:close(): graph: ud=%p already closed (%s %d)\n", (void *) ud, __FILE__, __LINE__);
405 lua_pushnumber(L, 0);
406 return 1;
410 /*-------------------------------------------------------------------------*\
411 * Property: name [string]
412 * Returns the name of a graph.
413 * Example:
414 * name = g.name
415 \*-------------------------------------------------------------------------*/
416 static int gr_nameof(lua_State *L)
418 gr_graph_t *ud = tograph(L, 1, STRICT);
419 if (ud->status != ALIVE){
420 luaL_error(L, "deleted");
421 return 0;
423 lua_pushstring(L, agnameof(ud->g));
424 return 1;
427 /*-------------------------------------------------------------------------*\
428 * Property: isstrict [boolean]
429 * Returns true is graph is strict, otherwise it returns false.
430 * Example:
431 * rv = g.isstrict
432 \*-------------------------------------------------------------------------*/
433 static int gr_isstrict(lua_State *L)
435 gr_graph_t *ud = tograph(L, 1, STRICT);
436 lua_pushboolean(L, agisstrict(ud->g));
437 return 1;
440 /*-------------------------------------------------------------------------*\
441 * Property: isdirected [boolean]
442 * Returns true if the graph is directed, otherwise it returns false
443 * Example:
444 * rv = g.isdirected
445 \*-------------------------------------------------------------------------*/
446 static int gr_isdirected(lua_State *L)
448 gr_graph_t *ud = tograph(L, 1, STRICT);
449 lua_pushboolean(L, agisdirected(ud->g));
450 return 1;
453 /*-------------------------------------------------------------------------* \
454 * Read a graph from a string
455 * Returns graph userdata.
456 * Example:
457 * g, err = graph.memread(str)
458 \*-------------------------------------------------------------------------*/
459 static int gr_memread(lua_State *L)
461 gr_graph_t *ud;
462 char *str = (char *)lua_tostring(L, 1);
464 ud = lua_newuserdata(L, sizeof(gr_graph_t));
465 if (!(ud->g = agmemread(str))){
466 lua_pushnil(L);
467 lua_pushstring(L, "agread failed");
468 return 2;
470 ud->name = strdup(agnameof(ud->g));
471 ud->type = AGRAPH;
472 ud->status = ALIVE;
474 /* We set callbacks only in root graph */
475 if (ud->g == agroot(ud->g)){
476 agpushdisc(ud->g, (struct Agcbdisc_s *)&disc, L);
478 set_object(L, ud->g);
479 return new_graph(L);
482 /*-------------------------------------------------------------------------* \
483 * Read a graph from a file; "stdin" reads from STDIN.
484 * Returns graph userdata.
485 * Example:
486 * g, err = graph.read(filename)
487 \*-------------------------------------------------------------------------*/
488 static int gr_read(lua_State *L)
490 gr_graph_t *ud;
491 FILE *fin;
492 char *fname = (char *)luaL_optstring(L, 1, "stdin");
494 if (!strcmp(fname, "stdin")){
495 fin = stdin;
496 } else {
497 fin = fopen(fname, "r");
499 if (!fin){
500 lua_pushnil(L);
501 lua_pushstring(L, "fopen failed");
502 return 2;
504 /* Create a userdata */
505 ud = lua_newuserdata(L, sizeof(gr_graph_t));
506 if (!(ud->g = agread(fin, NIL(Agdisc_t*)))){
507 fclose(fin);
508 lua_pushnil(L);
509 lua_pushstring(L, "agread failed");
510 return 2;
512 fclose(fin);
513 ud->name = strdup(agnameof(ud->g));
514 ud->type = AGRAPH;
515 ud->status = ALIVE;
517 /* We set callbacks only in root graph */
518 if (ud->g == agroot(ud->g)){
519 agpushdisc(ud->g, (struct Agcbdisc_s *)&disc, L);
521 set_object(L, ud->g);
522 return new_graph(L);
525 /*-------------------------------------------------------------------------*\
526 * Method: g.write(self, filename)
527 * Write a graph into a file.
528 * Returns 1 on success, nil plus error message on failure.
529 * rv, err = g:write(filename)
530 \*-------------------------------------------------------------------------*/
531 static int gr_write(lua_State *L)
533 int rv;
534 FILE *fout;
535 int stdflag = 0;
536 gr_graph_t *ud = tograph(L, 1, STRICT);
537 char *fname = (char*) luaL_optstring(L, 2, "__std__");
539 if (!strcmp(fname, "__std__")){
540 stdflag = 1;
541 fout = stdout;
542 } else {
543 fout = fopen(fname, "w+");
545 if (!fout){
546 lua_pushnil(L);
547 lua_pushstring(L, "fopen failed");
548 return 2;
550 rv = agwrite(ud->g, fout);
551 if (rv){
552 if (!stdflag)
553 fclose(fout);
554 lua_pushnil(L);
555 lua_pushstring(L, "agwrite failed");
556 return 2;
558 if (!stdflag)
559 fclose(fout);
560 lua_pushnumber(L, rv);
561 return 1;
564 /*-------------------------------------------------------------------------*\
565 * Property:nnodes [number]
566 * Provides the numebr of edges of a graph
567 * Example:
568 * rv = g.nnodes
569 \*-------------------------------------------------------------------------*/
570 static int gr_nnodes(lua_State *L)
572 gr_graph_t *ud = tograph(L, 1, STRICT);
573 lua_pushnumber(L, agnnodes(ud->g));
574 return 1;
577 /*-------------------------------------------------------------------------*\
578 * Property: nedges [number]
579 * Provides the number of edges of a graph.
580 * Example:
581 * rv = g.nedges
582 \*-------------------------------------------------------------------------*/
583 static int gr_nedges(lua_State *L)
585 gr_graph_t *ud = tograph(L, 1, STRICT);
586 lua_pushnumber(L, agnedges(ud->g));
587 return 1;
590 /*-------------------------------------------------------------------------*\
591 * Metamethod: __eq [boolean]
592 * Compare two graphs
593 * Example:
594 * g == h or g != h with metamethods
595 \*-------------------------------------------------------------------------*/
596 static int gr_equal(lua_State *L)
598 gr_graph_t *ud1 = tograph(L, 1, STRICT);
599 gr_graph_t *ud2 = tograph(L, 2, STRICT);
600 lua_pushboolean(L, (ud1->g == ud2->g));
601 return 1;
604 /*-------------------------------------------------------------------------*\
605 * Method: g.subgraph(self, name, nocreate)
606 * Find or create a subgraph. The optional flag nocreate=true inhibits creation.
607 * Returns graph userdata.
608 * Example:
609 * sg, err = g:subgraph(name)
610 \*-------------------------------------------------------------------------*/
611 static int gr_subgraph(lua_State *L)
613 int rv;
614 Agraph_t *g;
615 gr_graph_t *sg;
616 gr_graph_t *ud = tograph(L, 1, STRICT);
617 char *name = (char *) luaL_checkstring(L, 2);
619 /* Check wheter the subgraph already exists */
620 if ((g = agsubg(ud->g, name, 0)) != NULL){
622 /* Yes - return corresponing userdata */
623 rv = get_object(L, g);
624 if (lua_isnil(L, -rv)){
625 sg = lua_newuserdata(L, sizeof(gr_graph_t));
626 sg->g = g;
627 sg->name = strdup(name);
628 sg->type = AGRAPH;
629 sg->status = ALIVE;
630 return new_graph(L);
631 } else
632 return rv;
633 } else {
634 /* No - Create a new one */
635 if (lua_toboolean(L, 3)){
636 lua_pushnil(L);
637 lua_pushstring(L, "graph not found");
638 return 2;
640 sg = lua_newuserdata(L, sizeof(gr_graph_t));
641 if (!(sg->g = agsubg(ud->g, name, 1))){
642 lua_pushnil(L);
643 lua_pushstring(L, "agsubg failed");
644 return 2;
646 if (name)
647 sg->name = strdup(name);
648 sg->type = AGRAPH;
649 sg->status = ALIVE;
650 return new_graph(L);
654 /*-------------------------------------------------------------------------*\
655 * Property: root [userdata]
656 * Get root of graph.
657 * Example:
658 * sg, err = g.root
659 \*-------------------------------------------------------------------------*/
660 static int gr_root(lua_State *L)
662 gr_graph_t *ud = tograph(L, 1, STRICT);
663 Agraph_t *g;
665 if (!(g = agroot(ud->g))){
666 lua_pushnil(L);
667 lua_pushstring(L, "agroot failed");
669 lua_pop(L, 1); /* empty */
670 return get_object(L, g); /* root */
673 /*-------------------------------------------------------------------------*\
674 * Property: parent [userdata]
675 * Get parent of graph.
676 * Example:
677 * sg, err = g.parent
678 \*-------------------------------------------------------------------------*/
679 static int gr_parent(lua_State *L)
681 gr_graph_t *ud = tograph(L, 1, STRICT);
682 Agraph_t *g = agparent(ud->g);
684 return get_object(L, g);
687 /*-------------------------------------------------------------------------*\
688 * Method: g = getnext(self, prev)
689 * Retrieves next subgraph in a graph hierarchy.
690 * Returns the successor of the given graph. With nil for parameter prev the
691 * first successor is returned.
692 * Example:
693 * first = g:getnext(nil)
694 * second = g:getnext(first)
695 * third = g:getnext(second)
696 \*-------------------------------------------------------------------------*/
697 static int gr_getnext(lua_State *L)
699 int rv;
700 Agraph_t *g;
701 gr_graph_t *ud_sg;
702 gr_graph_t *ud = tograph(L, 1, STRICT);
703 if (lua_isnil(L, 2)){
704 g = agfstsubg(ud->g);
705 } else {
706 ud_sg = tograph(L, 2, STRICT);
707 g = agnxtsubg(ud_sg->g);
709 if (!g){
710 lua_pushnil(L);
711 return 1;
712 } else {
713 rv = get_object(L, g);
714 if (rv == 1)
715 return rv;
716 else {
717 lua_pop(L, rv);
718 ud_sg = lua_newuserdata(L, sizeof(gr_graph_t));
719 ud_sg->g = g;
720 ud_sg->name = strdup(agnameof(g));
721 ud_sg->type = AGRAPH;
722 ud_sg->status = ALIVE;
723 set_object(L, g);
724 return new_graph(L);
729 /*-------------------------------------------------------------------------*\
730 * Iterator: g.walk(self)
731 * Iterator over all subgraphs of the given graph.
732 * Example:
733 * for g in g:walk() do ... end
734 \*-------------------------------------------------------------------------*/
735 static int gr_walk(lua_State *L)
737 lua_pushcfunction(L, gr_getnext); /* ud, getnext - iterator func */
738 lua_pushvalue(L, -2); /* ud, getnext, g - state */
739 lua_pushnil(L); /* ud, getnext, g, nil - initial param = nil*/
740 return 3;
743 /*-------------------------------------------------------------------------*\
744 * Property: id [number]
745 * Get a graph's id.
746 * Example:
747 * n = g.id
748 \*-------------------------------------------------------------------------*/
749 static int gr_id(lua_State *L)
751 gr_graph_t *ud = tograph(L, 1, STRICT);
752 lua_pushnumber(L, AGID(ud->g));
753 return 1;
756 /*-------------------------------------------------------------------------*\
757 * Property: isroot [boolean]
758 * Is given graph a root ?
759 * Example:
760 * b = g.isroot
761 \*-------------------------------------------------------------------------*/
762 static int gr_isroot(lua_State *L)
764 gr_graph_t *ud = tograph(L, 1, STRICT);
765 lua_pushboolean(L, agroot(ud->g) == ud->g);
766 return 1;
769 /*-------------------------------------------------------------------------*\
770 * Method: t, err = g.getgraphattr(self),
771 * t, err = g.getnodeattr(self),
772 * t, err = g.getedgeattr(self)
773 * Reads default attributes into a Lua table.
774 * Example:
775 * tab, err = g:getnodeattr()
776 \*-------------------------------------------------------------------------*/
777 static int gr_getgraphattr(lua_State *L)
779 return getattr(L, AGRAPH);
782 static int gr_getnodeattr(lua_State *L)
784 return getattr(L, AGNODE);
787 static int gr_getedgeattr(lua_State *L)
789 return getattr(L, AGEDGE);
792 /*-------------------------------------------------------------------------*\
793 * Method: t, err = g.getattr(self)
794 * Reads declarations/default attributes into a nested Lua table for format:
795 * t = {graph={name=val, ...}, node={name=val}, edge={name=val}}
796 * Example:
797 * tab, err = g:getattr()
798 \*-------------------------------------------------------------------------*/
799 static int gr_getattr(lua_State *L)
801 lua_newtable(L); /* ud, t */
803 lua_pushstring(L, "graph"); /* ud, t, key */
804 getattr(L, AGRAPH); /* ud, t, key, gt */
805 lua_settable(L, -3); /* ud, t */
807 lua_pushstring(L, "node"); /* ud, t, key */
808 getattr(L, AGNODE); /* ud, t, key, nt, n */
809 lua_settable(L, -3); /* ud, t */
811 lua_pushstring(L, "edge"); /* ud, t, key */
812 getattr(L, AGEDGE); /* ud, t, key, et, n */
813 lua_settable(L, -3); /* ud, t */
815 return 1;
818 /*-------------------------------------------------------------------------*\
819 * Method: n, err = g.setgraphattr(self, t),
820 * n, err = g.setnodeattr(self, t),
821 * n, err = g.setedgeattr(self, t)
822 * Sets default attributes from a Lua table t.
823 * Returns the number of attributes set.
824 * Example:
825 * rv, err = g:setnodeattr{shape="box", width=12}
826 \*-------------------------------------------------------------------------*/
827 static int gr_setgraphattr(lua_State *L)
829 return setattr(L, AGRAPH);
832 static int gr_setnodeattr(lua_State *L)
834 return setattr(L, AGNODE);
837 static int gr_setedgeattr(lua_State *L)
839 return setattr(L, AGEDGE);
842 /*-------------------------------------------------------------------------* \
843 * Method: n, err = g.setattr(self, t)
844 * Sets default attributes from a nested Lua table of format:
845 * see g.getattr(self) above.
846 * Returns the number of attributes set.
847 * Example:
848 * rv, err = g:setattr{graph={...}, node={shape="box"}, edge={...}}
849 \*-------------------------------------------------------------------------*/
850 static int gr_setattr(lua_State *L)
852 int n = 0;
853 int rv;
855 /* 1. Retrieve object specific attribute table from attribute table. */
856 lua_pushstring(L, "graph"); /* ud, t, key */
857 lua_gettable(L, -2); /* ud, t, gt */
858 if (!lua_isnil(L, -1)){
859 /* 2. Set/declare all object specific attribute */
860 rv = setattr(L, AGRAPH); /* ud, t, gt, n */
861 n += (int) lua_tonumber(L, -rv);
862 lua_pop(L, 2); /* ud, t */
863 } else
864 lua_pop(L, 1); /* ud, t */
866 /* 1. ... see above */
867 lua_pushstring(L, "node"); /* ud, t, key */
868 lua_gettable(L, -2); /* ud, t, nt */
869 if (!lua_isnil(L, -1)){
870 /* 2. ... see above */
871 rv = setattr(L, AGNODE); /* ud, t, nt, n */
872 n += (int) lua_tonumber(L, -rv);
873 lua_pop(L, 2); /* ud, t */
874 } else
875 lua_pop(L, 1); /* ud, t */
877 /* 1. ... see above */
878 lua_pushstring(L, "edge"); /* ud, t, key */
879 lua_gettable(L, -2); /* ud, t, et */
880 if (!lua_isnil(L, -1)){
881 /* 2. ... see above */
882 rv = setattr(L, AGEDGE); /* ud, t, et, n */
883 n += (int) lua_tonumber(L, -rv);
884 lua_pop(L, 2); /* ud, t */
885 } else
886 lua_pop(L, 1);
888 lua_pushnumber(L, n); /* ud, t, n */
889 return 1;
892 /*-------------------------------------------------------------------------*\
893 * Method: g.delete(self, obj)
894 * Deletes an object from the given graph.
895 * Returns non-nil.
896 * Example:
897 * rv, err = g:delete(n)
898 \*-------------------------------------------------------------------------*/
899 static int gr_delete(lua_State *L)
901 gr_object_t *obj = toobject(L, 2, NULL, STRICT);
902 TRACE(" g.delete(): obj: ud=%p ptr=%p type=%d (%s %d)\n",
903 (void *) obj, (void *) obj->p.p, AGTYPE(obj->p.p), __FILE__, __LINE__);
905 switch(AGTYPE(obj->p.p)){
906 case AGRAPH:
907 TRACE(" g.delete(): graph: ud=%p '%s' g=%p (%s %d)\n",
908 (void *)obj, agnameof(obj->g.g), (void *)obj->g.g, __FILE__, __LINE__);
909 lua_pushcfunction(L, gr_close); /* ud, obj, func */
910 lua_pushvalue(L, 2); /* ud, obj, func, obj */
911 lua_call(L, 1, 1); /* ud, obj, result */
912 lua_pop(L, 1); /* ud, obj */
913 break;
914 case AGNODE:
915 TRACE(" g:delete(): node: ud=%p '%s' g=%p (%s %d)\n",
916 (void *)obj, agnameof(obj->n.n), (void *)obj->n.n, __FILE__, __LINE__);
917 lua_pushcfunction(L, gr_delete_node);
918 lua_pushvalue(L, 2); /* ud, obj, func, obj */
919 lua_call(L, 1, 1); /* ud, obj, result */
920 lua_pop(L, 1); /* ud, obj */
921 break;
922 case AGEDGE:
923 TRACE(" g:delete(): edge: ud=%p '%s' g=%p (%s %d)\n",
924 (void *)obj, obj->p.name, (void *)obj->e.e, __FILE__, __LINE__);
925 lua_pushcfunction(L, gr_delete_edge);
926 lua_pushvalue(L, 2); /* ud, obj, func, obj */
927 lua_call(L, 1, 1); /* ud, obj, result */
928 lua_pop(L, 1); /* ud, obj */
929 break;
931 lua_pushnumber(L, 0);
932 return 1;
936 * A simple wrapper for usage of gr_node in other files.
938 int gr_create_node(lua_State *L)
940 return gr_node(L);
943 /*-------------------------------------------------------------------------* \
944 * Method: n, err = g.node(self [, name[, nocreate]])
945 * Finds or creates a node of given name. If name is nil autoname 'node@ID'
946 * is used. The optional flag nocreate=true inhibits auto-creation.
947 * Returns userdata.
948 * Examples:
949 * n, err = g:node("node-1", true)
950 * - Node will not be created, if it does not exists.
951 * n, err = g:node("node2")
952 * - Node will be created, if it does not exist.
953 * n, err = g:node()
954 * -
955 \*-------------------------------------------------------------------------*/
956 static int gr_node(lua_State *L)
958 Agnode_t *n;
959 gr_node_t *node;
960 int rv;
961 char *name;
963 /* param 1: graph = self */
964 gr_graph_t *ud = tograph(L, 1, STRICT);
966 name = (char *) luaL_checkstring(L, 2); /* ud, name */
968 if (name && (n = agnode(ud->g, name, 0)) != NULL){
969 rv = get_object(L, n); /* ud, name, node/nil */
970 if (lua_isnil(L, -rv)){
971 lua_pop(L, rv); /* ud, name */
972 /* Node not yet registered */
973 node = lua_newuserdata(L, sizeof(gr_node_t)); /* ud, name, node */
974 node->n = n;
975 node->name = strdup(agnameof(n));
976 node->type = AGNODE;
977 node->status = ALIVE;
978 return new_node(L);
979 } else
980 /* Node already registered */
981 return rv; /* ud, name, node */
982 } else {
983 /* Node does not exist */
984 if (lua_toboolean(L, 3)){
985 /* auto-creation forbidden: return an error */
986 lua_pushnil(L);
987 lua_pushstring(L, "node not found");
988 return 2;
990 /* Enforce creation with given name or an intermediate name '__tempname__' */
991 node = lua_newuserdata(L, sizeof(gr_node_t));
992 if ( (name && (node->n = agnode(ud->g, name, 1)) == NULL)){
993 luaL_error(L, "agnode failed");
994 return 0;
996 node->name = strdup(name);
997 node->type = AGNODE;
998 node->status = ALIVE;
999 return new_node(L);
1003 /*-------------------------------------------------------------------------* \
1004 * Method: e, tail, head = g.edge(self, tail, head, label, nocreate)
1005 * Finds or creates an edge from tail to head with label. Parameter label
1006 * is optional.
1007 * The optional flag nocreate inhibits auto-creation of the edge.
1008 * Any node given by name is implicitly created and it's userdata returned
1009 * as additional results - even if nocreate is not set.
1010 * Userdata proxy object is registered on the fly, if not yet available.
1011 * Examples:
1012 * e, tail, head = g:edge(n1, "node@2", "n1 => n2"[, false|true])
1013 * e, tail, head = g:edge(n1, n2)
1014 * e, tail, head = g:edge("node@1", n2, nil, true)
1015 \*-------------------------------------------------------------------------*/
1016 static int gr_edge(lua_State *L)
1018 Agedge_t *e;
1019 gr_edge_t *edge;
1020 gr_node_t *tail, *head;
1021 int rv;
1022 char ename[32];
1023 char *label;
1024 int head_created = 0;
1025 int tail_created = 0;
1027 /* param 1: graph = self */
1028 gr_graph_t *ud = tograph(L, 1, STRICT);
1030 /* param 2: tail node */
1031 if (lua_isuserdata(L, 2) == 1) {
1032 tail = tonode(L, 2, STRICT);
1033 } else {
1034 /* Create a node given only by name */
1035 lua_pushcfunction(L, gr_node); /* ud, ntail, (n)head, [label], func */
1036 lua_pushvalue(L, 1); /* ud, ntail, (n)head, [label], func, ud */
1037 lua_pushstring(L, (const char *) luaL_checkstring(L, 2));
1038 /* g.node(self, name, flag) */
1039 lua_call(L, 2, 1); /* ud, ntail, (n)head, (label), tail */
1040 if (lua_isnil(L, -1)){
1041 return luaL_error(L, "tailnode create failed");
1044 tail = tonode(L, -1, STRICT);
1045 lua_pop(L, 1); /* ud, ntail, (n)head, (label) */
1046 tail_created = 1;
1048 /* param 3: head node */
1049 if (lua_isuserdata(L, 3))
1050 head = tonode(L, 3, STRICT);
1051 else {
1052 /* Create a node given only by name */
1053 lua_pushcfunction(L, gr_node); /* ud, ntail, (n)head, [label], func */
1054 lua_pushvalue(L, 1); /* ud, ntail, (n)head, [label], func, ud */
1055 lua_pushstring(L, (const char *) luaL_checkstring(L, 3));
1056 /* g.node(self, name, flag) */
1057 lua_call(L, 2, 1); /* ud, ntail, (n)head, (label), head */
1058 if (lua_isnil(L, -1)){
1059 del_object(L, tail);
1060 return luaL_error(L, "headnode create failed");
1062 head = tonode(L, -1, STRICT);
1063 lua_pop(L, 1); /* ud, ntail, (n)head, (label) */
1064 head_created = 1;
1067 /* param 4: label */
1068 label = (char *) luaL_optstring(L, 4, NULL); /* ud, tail, head */
1070 /* Check whether edge already exists - only required for strict graphs */
1071 if ((e = agedge(ud->g, tail->n, head->n, NULL, 0)) != NULL){
1072 TRACE(" gr_edge(): edge '%s' exists force=%d (%s %d)\n", agnameof(e), (int) lua_toboolean(L, 5), __FILE__, __LINE__);
1073 /* Edge exists */
1074 if (agisstrict(ud->g)) {
1075 /* strict directed graph: give edge a new label */
1076 agsafeset(e, "label", label, NULL);
1078 rv = get_object(L, e);
1079 if (lua_isnil(L, -rv)){
1080 rv = get_object(L, agopp(e));
1081 if (lua_isnil(L, -rv))
1082 return rv;
1084 lua_pushlightuserdata(L, tail);
1085 lua_pushlightuserdata(L, head);
1086 return 3;
1088 /* Edge doees not exist */
1089 if (lua_isboolean(L, 5)){
1090 /* creation of edge forbidden: nocreate flag is set */
1091 if (tail_created)
1092 del_object(L, tail->n);
1093 if (head_created)
1094 del_object(L, head->n);
1095 lua_pushnil(L);
1096 lua_pushstring(L, "edge not found");
1097 return 2;
1099 /* Enforce creation */
1100 edge = lua_newuserdata(L, sizeof(gr_edge_t));
1101 if (agroot(tail->n) != agroot(head->n)){
1102 luaL_error(L, "nodes in different graphs");
1103 return 0;
1105 sprintf(ename, "edge@%u", newid());
1106 if (!(edge->e = agedge(ud->g, tail->n, head->n, ename, 1))){
1107 /* creation failed */
1108 if (tail_created)
1109 del_object(L, tail->n);
1110 if (head_created)
1111 del_object(L, head->n);
1112 lua_pushnil(L);
1113 lua_pushstring(L, "agedge failed");
1114 return 2;
1116 TRACE(" gr_edge(): ud=%p '%s' l=%p g=%p e=%p tail=%p head=%p force=%d (%s %d)\n",
1117 (void *) edge, label, (void*) label, (void *)ud->g, (void *)edge->e,
1118 (void *)tail->n, (void *)head->n, (int) lua_toboolean(L, 5), __FILE__, __LINE__);
1119 if (label)
1120 agsafeset(edge->e, "label", label, NULL);
1121 edge->name = strdup(ename);
1122 edge->type = AGEDGE;
1123 edge->status = ALIVE;
1124 new_edge(L);
1125 lua_pushlightuserdata(L, tail);
1126 lua_pushlightuserdata(L, head);
1127 return 3;
1130 /*-------------------------------------------------------------------------*\
1131 * Method: n, err = g.findedge(self, tail, head [, name])
1132 * Finds an edge of given name between given nodes.
1133 * If multiple edges exist in non-directed graph, onle one of them is
1134 * retrieved, if name is not specified.
1135 * Returns proxy userdata.
1136 * Example:
1137 * e, tail, head = g:findedge(n1, "node@2", "edge@122")
1138 \*-------------------------------------------------------------------------*/
1139 static int gr_findedge(lua_State *L)
1141 Agedge_t *e;
1142 gr_edge_t *edge;
1143 int rv;
1144 char sbuf[32];
1145 gr_graph_t *ud = tograph(L, 1, STRICT);
1146 gr_node_t *tail = tonode(L, 2, STRICT);
1147 gr_node_t *head = tonode(L, 3, STRICT);
1148 char *name = (char *) luaL_optstring(L, 4, NULL); /* ud, tail, head, label */
1149 if ((e = agedge(ud->g, tail->n, head->n, name, 0)) != NULL){
1150 rv = get_object(L, e); /* ud, tail, head, (label), e/nil */
1151 if (lua_isnil(L, -rv)){
1152 rv = get_object(L, agopp(e)); /* ud, tail, head, (label), e/nil, nil/e */
1153 if (lua_isnil(L, -rv)){
1154 lua_pop(L, rv); /* ud, tail, head, (label) */
1155 /* Edge not yet registered */
1156 edge = lua_newuserdata(L, sizeof(gr_edge_t)); /* ud, peer, name, edge */
1157 edge->e = e;
1158 sprintf(sbuf, "edge@%lu", (unsigned long) AGID(e));
1159 edge->name = strdup(sbuf);
1160 edge->type = AGEDGE;
1161 edge->status = ALIVE;
1162 set_object(L, e); /* ud, peer, name, edge */
1163 new_edge(L);
1164 lua_pushlightuserdata(L, tail);
1165 lua_pushlightuserdata(L, head);
1166 return 3;
1167 } else {
1168 /* Edge already registered */
1169 lua_pushlightuserdata(L, tail);
1170 lua_pushlightuserdata(L, head);
1171 return rv + 2; /* ud, peer, name, edge */
1173 } else {
1174 /* Edge already registered */
1175 lua_pushlightuserdata(L, tail);
1176 lua_pushlightuserdata(L, head);
1177 return rv + 2; /* ud, peer, name, edge */
1179 } else {
1180 lua_pushnil(L);
1181 lua_pushstring(L, "edge not found");
1182 return 2;
1185 /*-------------------------------------------------------------------------*\
1186 * Method: n, err = g.idnode(self, id)
1187 * Finds a node by id.
1188 * Returns userdata.
1189 * Example:
1190 * n, err = g:idnode(18)
1191 \*-------------------------------------------------------------------------*/
1192 static int gr_idnode(lua_State *L)
1194 Agnode_t *n;
1195 gr_graph_t *ud = tograph(L, 1, STRICT);
1196 unsigned long id = (unsigned long) luaL_checknumber(L, 2);
1197 if ((n = agidnode(ud->g, id, 0)) != NULL){
1198 return get_object(L, n);
1199 } else {
1200 lua_pushnil(L);
1201 lua_pushstring(L, "agidnode failed");
1202 return 2;
1206 /*-------------------------------------------------------------------------*\
1207 * Method: n = nextnode(self, prev)
1208 * Retrieves next node in a graph.
1209 * Returns the successor of the given node. With nil as parameter prev the
1210 * first node is returned.
1211 * Example:
1212 * first = g:nextnode(nil)
1213 * second = g:nextnode(first)
1214 * third = g:nextnode(second)
1215 \*-------------------------------------------------------------------------*/
1216 static int gr_nextnode(lua_State *L)
1218 int rv;
1219 Agnode_t *n;
1220 gr_node_t *ud_n;
1221 gr_graph_t *ud_g = tograph(L, 1, STRICT);
1222 if (lua_isnil(L, 2))
1223 n = agfstnode(ud_g->g);
1224 else {
1225 ud_n = tonode(L, 2, STRICT);
1226 n = agnxtnode(ud_g->g, ud_n->n);
1228 if (!n){
1229 /* no more nodes */
1230 lua_pushnil(L);
1231 return 1;
1232 } else {
1233 /* Check whether userdata exists .. */
1234 rv = get_object(L, n);
1235 if (rv == 1)
1236 /* .. yes: return it */
1237 return rv;
1238 else {
1239 /* .. no: create it */
1240 lua_pop(L, rv);
1241 ud_n = lua_newuserdata(L, sizeof(gr_node_t));
1242 ud_n->n = n;
1243 ud_n->name = strdup(agnameof(n));
1244 ud_n->type = AGNODE;
1245 ud_n->status = ALIVE;
1246 set_object(L, n);
1247 return new_node(L);
1252 /*-------------------------------------------------------------------------*\
1253 * Iterator: walknodes()
1254 * Iterator over all nodes of a given graph.
1255 * Example:
1256 * for n in g:walknodes() do ... end
1257 \*-------------------------------------------------------------------------*/
1258 static int gr_walknodes(lua_State *L)
1260 lua_pushcfunction(L, gr_nextnode); /* ud, nextnode */
1261 lua_pushvalue(L, -2); /* ud, nextnode, g */
1262 lua_pushnil(L); /* ud, nextnode, g, nil */
1263 return 3;
1266 /*-------------------------------------------------------------------------*\
1267 * Property: g.graph [userdata]
1268 * Retrieves the graph to which given subgraph belongs. Trivial because it
1269 * returns itself.
1270 * Returns graph userdata.
1271 * Example:
1272 * g, err = g.graph
1273 \*-------------------------------------------------------------------------*/
1274 int gr_graphof(lua_State *L)
1276 gr_graph_t *ud = tograph(L, 1, STRICT);
1277 Agraph_t *g = agraphof(ud->g);
1278 if (g == NULL){
1279 lua_pushnil(L);
1280 lua_pushstring(L, "no graph");
1281 return 2;
1283 return get_object(L, ud->g);
1286 /*-------------------------------------------------------------------------*\
1287 * Method: rv = g.contains(self, obj)
1288 * Returns true if graph g contains object obj.
1289 * Example:
1290 * b = g:contains(n)
1291 \*-------------------------------------------------------------------------*/
1292 int gr_contains(lua_State *L)
1294 gr_graph_t *ud = tograph(L, 1, STRICT);
1295 gr_object_t *obj = toobject(L, 2, NULL, STRICT);
1296 int rv = agcontains(ud->g, obj->p.p);
1297 if (rv && AGTYPE(obj->p.p) == AGNODE){
1298 if (agroot(obj->n.n) == agroot(ud->g))
1299 lua_pushboolean(L, TRUE);
1300 else
1301 lua_pushboolean(L, FALSE);
1302 } else
1303 lua_pushboolean(L, rv);
1304 return 1;
1307 /*-------------------------------------------------------------------------*\
1308 * Method: rv = g.layout(self, fmt)
1309 * Layout the given graph in the specified format/algorithm.
1310 * Example:
1311 * b = g:layout("dot")
1312 \*-------------------------------------------------------------------------*/
1313 static int gr_layout(lua_State *L)
1315 int rv;
1316 gr_graph_t *ud = tograph(L, 1, STRICT);
1317 char *fmt = (char *) luaL_optstring(L, 2, "dot");
1319 if (!strcmp(fmt, "dot") ||
1320 !strcmp(fmt, "neato") ||
1321 !strcmp(fmt, "nop") ||
1322 !strcmp(fmt, "nop2") ||
1323 !strcmp(fmt, "twopi") ||
1324 !strcmp(fmt, "fdp") ||
1325 !strcmp(fmt, "circo")){
1327 if ((rv = gv_layout(ud->g, fmt)) != GR_SUCCESS){
1328 luaL_error(L, "layout error: %d", rv);
1329 return 0;
1331 lua_pushvalue(L, rv);
1332 return 1;
1333 } else {
1334 luaL_error(L, "invalid layout format '%s'", fmt);
1335 return 0;
1339 /*-------------------------------------------------------------------------*\
1340 * Method: rv = g.freelayout(self, fmt)
1341 * Free a layout
1342 * Example:
1343 * b = g:freelayout()
1344 \*-------------------------------------------------------------------------*/
1345 static int gr_freelayout(lua_State *L)
1347 gr_graph_t *ud = tograph(L, 1, STRICT);
1348 if (ud->g)
1349 gv_free_layout(ud->g);
1350 else {
1351 lua_pushnil(L);
1352 lua_pushstring(L, "invalid graph");
1353 return 2;
1355 lua_pushnumber(L, 0);
1356 return 1;
1358 /*-------------------------------------------------------------------------*\
1359 * Method: rv = g.render(self, rfmt, file, lfmt)
1360 * Render the given graph in the specified format.
1361 * Example:
1362 * b = g:render("pdf")
1363 \*-------------------------------------------------------------------------*/
1364 static int gr_render(lua_State *L)
1366 int rv;
1367 gr_graph_t *ud = tograph(L, 1, STRICT);
1368 char *rfmt = (char *) luaL_optstring(L, 2, "plain");
1369 char *fname = (char *) luaL_optstring(L, 3, NULL);
1370 char *lfmt = (char *) luaL_optstring(L, 4, NULL);
1371 if (gvc == NULL){
1372 lua_pushnil(L);
1373 lua_pushstring(L, "layout missing");
1374 return 2;
1376 if (lfmt)
1377 gv_layout(ud->g, lfmt);
1378 if (fname)
1379 rv = gv_render_file(ud->g, rfmt, fname);
1380 else
1381 rv = gv_render(ud->g, rfmt, stdout);
1382 if (lfmt)
1383 gv_free_layout(ud->g);
1384 if (rv != GR_SUCCESS){
1385 lua_pushnil(L);
1386 lua_pushstring(L, "gvRender failed");
1387 return 2;
1389 lua_pushnumber(L, rv);
1390 return 1;
1393 /*-------------------------------------------------------------------------*\
1394 * Method: list, count = graph.plugins(type)
1395 * Retrieve available plugins for layout or rendering.
1396 * type: layout | render
1397 * Example:
1398 * tab, err = g:layout("layout")
1399 \*-------------------------------------------------------------------------*/
1400 static int gr_plugins(lua_State *L)
1402 int i, count;
1403 char **list;
1405 char *kind = (char *) luaL_optstring(L, 1, "render");
1406 list = gvPluginList(gvc, kind, &count, NULL);
1407 if (list == NULL){
1408 lua_pushnil(L);
1409 lua_pushstring(L, "no plugins");
1410 return 2;
1413 lua_newtable(L); /* t */
1414 for (i = 0; i < count; i++){
1415 lua_pushnumber(L, i+1); /* t, index */
1416 lua_pushstring(L, list[i]); /* t, index, value */
1417 lua_settable(L, -3); /* t */
1419 lua_pushnumber(L, count);
1420 for (i = 0; i < count; i++)
1421 free(list[i]);
1422 free(list);
1423 return 2;
1425 /*-------------------------------------------------------------------------*\
1426 * Module initialization
1427 \*-------------------------------------------------------------------------*/
1428 LUALIB_API int luaopen_graph_core(lua_State *L) {
1429 lua_newtable(L);
1430 lua_pushvalue(L, -1);
1431 lua_setglobal(L, MYNAME);
1432 register_metainfo(L, funcs);
1433 lua_pushliteral(L, "version");
1434 lua_pushliteral(L, MYVERSION);
1435 lua_rawset(L, -3);
1436 lua_pushliteral(L, "_VERSION");
1437 lua_pushliteral(L, MYVERSION);
1438 lua_rawset(L, -3);
1439 lua_pushliteral(L, "_GVVERSION");
1440 lua_pushliteral(L, MYGVVERSION);
1441 lua_rawset(L, -3);
1442 lua_pushliteral(L, "_COPYRIGHT");
1443 lua_pushliteral(L, MYCOPYRIGHT);
1444 lua_rawset(L, -3);
1445 lua_pushliteral(L, "_DESCRIPTION");
1446 lua_pushliteral(L, MYDESCRIPTION);
1447 lua_rawset(L, -3);
1448 lua_pushliteral(L, "_SYSTEM");
1449 lua_pushliteral(L, MYSYSTEM);
1450 lua_rawset(L, -3);
1451 lua_pushliteral(L, "plugins");
1452 lua_pushcfunction(L, gr_plugins);
1453 lua_rawset(L, -3);
1454 if ((gvc = gvContextPlugins(lt_preloaded_symbols, DEMAND_LOADING)) == NULL){
1455 return luaL_error(L, "cannot load plugins");
1457 return 1;
1460 /*-------------------------------------------------------------------------*\
1461 * Metamethod: __tostring [boolean]
1462 * Return a string presentation of a graph object.
1463 * Example:
1464 * g == h or g != h with metamethods
1465 \*-------------------------------------------------------------------------*/
1466 static int gr_tostring(lua_State *L)
1468 gr_object_t *ud = toobject(L, 1, NULL, NONSTRICT);
1469 lua_pushfstring(L, "graph: %p (%s)", ud, ud->p.status == ALIVE ? "alive" : "dead");
1470 return 1;