currently only works with Lua 5.1 from LuaForWindows - compiler version problem
[luagraph.git] / src / gr_util.c
blobde39fdc9ecfac971e4e827d526ea874379287ca9
1 /*=========================================================================*\
2 * LuaGRAPH toolkit
3 * Graph support for Lua.
4 * Herbert Leuwer
5 * 30-7-2006, 01/2017
7 * Utilities
9 \*=========================================================================*/
11 /*=========================================================================*\
12 * Includes
13 \*=========================================================================*/
14 #include <string.h>
15 #include <stdlib.h>
16 #include "lua.h"
17 #include "lauxlib.h"
18 #include "gr_graph.h"
20 /*=========================================================================* \
21 * Functions
22 \*=========================================================================*/
23 static unsigned int localid = 0;
25 unsigned int newid(void){
26 localid = localid + 1;
27 return localid;
31 * Convert numerical kind indication into readable text string.
33 char *agobjkindstr(struct Agobj_s *obj)
35 int kind = agobjkind(obj);
36 return kind == AGRAPH ? "graph" : kind == AGNODE ? "node" : kind == AGEDGE ? "edge" : "unknown";
40 * Insertion callback: called when an object is inserted by cgraph.
41 * Used to register proxy object on top of stack in Lua registry using cgraph object as key.
43 void cb_insert(struct Agraph_s *g, struct Agobj_s *obj, void *L)
45 /* register user data on top of stack */
46 TRACE(" cb_insert(): register object '%s' type: %s\n", agnameof(obj), agobjkindstr(obj));
47 set_object((lua_State *)L, (void *) obj);
51 * Deletion callback: called when an object is inserted by cgraph.
52 * De-register proxy object.
54 void cb_delete(Agraph_t *g, Agobj_t *obj, void *L)
56 char *skey;
58 TRACE(" cb_delete() : unregister object '%s' kind=%s ptr=%p\n", agnameof(obj),
59 agobjkind(obj) == AGRAPH ? "graph" : agobjkind(obj) == AGNODE ? "node" :
60 agobjkind(obj) == AGEDGE ? "edge" : "unknown", obj);
61 skey = agget(obj, "__attrib__");
62 if (skey && (strlen(skey) != 0)) {
63 printf(" cb_delete(): nulling attrib\n");
64 lua_pushstring(L, skey);
65 lua_pushnil(L);
66 lua_rawset(L, LUA_REGISTRYINDEX);
67 agset(obj, "__attrib__", NULL);
68 agstrfree(agroot(obj), skey);
70 del_object(L, obj);
74 * Modification callback. Not used by LuaGRAPH
75 */
76 void cb_modify(Agraph_t *g, Agobj_t *obj, void *L, Agsym_t *sym)
78 TRACE(" cb_modify(): modify object '%s'\n", agnameof(obj));
82 * Query Lua stack for an object
84 gr_object_t *toobject(lua_State *L, int narg, const char *type, int strict)
86 gr_object_t *ud;
88 if (type != NULL){
89 ud = luaL_checkudata(L, narg, type);
90 } else {
91 ud = lua_touserdata(L, narg);
93 if (!ud){
94 luaL_error(L, "bad argument %d (valid userdata `%s' expected; null received)",
95 narg, (type) ? type : "???");
96 return NULL;
98 if (strict && (ud->p.p == NULL)){
99 luaL_error(L, "bad argument #%d (valid userdata `%s' %d expected: invalid received)",
100 narg, (type) ? type : "???", ud->p.status);
101 lua_error(L);
102 return NULL;
104 return ud;
108 * Register an object in Lua registry.
110 int set_object(lua_State *L, void *key)
112 TRACE(" set_object(): key=%p (%s %d)\n", key, __FILE__, __LINE__);
113 lua_pushlightuserdata(L, key); /* ud, key */
114 lua_pushvalue(L, -2); /* ud, key, ud */
115 lua_rawset(L, LUA_REGISTRYINDEX); /* ud */
116 return 1;
119 int set_status(lua_State *L, void *key, int status)
121 gr_object_t *ud;
123 lua_pushlightuserdata(L, key); /* ?, key */
124 lua_rawget(L, LUA_REGISTRYINDEX);
125 ud = lua_touserdata(L, -1);
126 if (ud)
127 ud->p.status = status;
128 return 0;
131 * Delete an object from Lua registry.
133 int del_object(lua_State *L, void *key)
135 TRACE(" del_object(): key=%p '%s' (%s %d)\n", key, agnameof(key), __FILE__, __LINE__);
136 set_status(L, key, DEAD);
137 lua_pushlightuserdata(L, key);
138 lua_pushnil(L); /* ?, key, nil */
139 lua_rawset(L, LUA_REGISTRYINDEX); /* ? */
140 return 0;
143 /*-------------------------------------------------------------------------*\
144 * Property: status [string]
145 * Returns the string "alive" or "dead" for the given graph.
146 * Use to test whether a reference to a graph object kept in a lua variable
147 * is still alive.
148 * Example:
149 * b = g.status
150 \*-------------------------------------------------------------------------*/
151 int gr_status(lua_State *L)
153 gr_object_t *ud = toobject(L, 1, NULL, NONSTRICT);
154 if (ud){
155 if (ud->p.status == ALIVE)
156 lua_pushstring(L, "alive");
157 else
158 lua_pushstring(L, "dead");
159 } else {
160 lua_pushstring(L, "???");
162 return 1;
165 int gr_collect(lua_State *L)
167 gr_object_t *ud = lua_touserdata(L, 1);
168 TRACE(" gr_collect(): %s ud=%p ptr=%p\n", ud->p.name, ud, ud->p.p);
169 if (ud->p.name)
170 free(ud->p.name);
171 ud->p.p = NULL;
172 return 0;
176 * Get an object from Lua registry.
178 int get_object(lua_State *L, void *key)
180 gr_object_t *ud;
181 lua_pushlightuserdata(L, key); /* ?, key */
182 lua_rawget(L, LUA_REGISTRYINDEX); /* ?, ud or nil */
183 ud = lua_touserdata(L, -1);
184 if (ud == NULL){
185 TRACE(" get_object(): key = %p not found (%s %d)\n", key, __FILE__, __LINE__);
186 lua_pushstring(L, "object not found in registry"); /* ?, nil, err */
187 return 2;
189 TRACE(" get_object(): key=%p ud=%p '%s' (%s %d)\n", key, ud, ud->p.name, __FILE__,__LINE__);
190 return 1; /* ?, ud */
194 * Get the value of a graphviz object attribute
196 int getval(lua_State *L)
198 gr_object_t *ud = toobject(L, 1, NULL, STRICT);
199 char *key = (char *) luaL_checkstring(L, 2);
200 char *value = agget(ud->p.p, key);
201 if (!value || strlen(value) == 0){
202 lua_pushnil(L);
203 return 1;
205 lua_pushstring(L, value);
206 return 1;
210 * Set a value to a graphviz object attribute
212 static int setval(lua_State *L)
214 gr_object_t *ud = toobject(L, 1, NULL, STRICT);
215 char *key = (char *) luaL_checkstring(L, 2);
216 char *value = (char *) luaL_checkstring(L, 3);
217 // return agset(ud->p.p, key, value);
218 return agsafeset(ud->p.p, key, value, "");
222 * Generic object index metamethod handler
223 * This closure has 2 upvalues:
224 * 1. table with read members (this is the metatable)
225 * 2. metatable.__metatable contains the methods
226 * Lua stack: ud, key
228 int object_index_handler(lua_State *L)
230 gr_object_t *ud;
231 char *skey;
233 /* Read member lookup in first upvalue */
234 lua_pushvalue(L, 2); /* ud, key, key */
235 lua_rawget(L, lua_upvalueindex(1)); /* ud, key, getfunc (member) or nil (method) */
236 if (!lua_isfunction(L, -1)){
237 /* Member not found - try methods */
238 lua_pop(L, 1); /* ud, key */
239 lua_pushvalue(L, 2); /* ud, key, key */
240 lua_gettable(L, lua_upvalueindex(2)); /* ud, key, method */
241 if (lua_isnil(L, -1)){
242 /* Try generic get method */
243 if (lua_isstring(L, 2)){
244 lua_settop(L, 2); /* ud, key */
245 lua_pushcfunction(L, getval); /* ud, key, gr_get */
246 lua_pushvalue(L, 1); /* ud, key, gr_get, ud */
247 lua_pushvalue(L, 2); /* ud, key, gr_get, ud, key */
248 lua_call(L, 2, 1); /* ud, key, value or nil */
250 if (lua_isnil(L, -1)){
251 /* Try generic storage */
252 lua_settop(L, 2); /* ud, key */
253 ud = toobject(L, 1, NULL, STRICT);
254 skey = agget(ud->p.p, "__attrib__");
255 if (skey && strlen(skey) > 0){
256 lua_pushstring(L, skey); /* ud, key, skey */
257 lua_rawget(L, LUA_REGISTRYINDEX); /* ud, key, stab */
258 lua_pushvalue(L, 2); /* ud, key, stab, key */
259 lua_rawget(L, -2); /* ud, key, stab, value */
260 lua_remove(L, -2); /* ud, key, value */
261 } else
262 lua_pushnil(L); /* ud, key, nil */
265 return 1;
267 /* Call the member's access function */
268 lua_pushvalue(L, 1); /* ud, key, getfunc, ud */
269 lua_pushvalue(L, 2); /* ud, key, getfunc, ud, key */
270 lua_call(L, 2, 1); /* ud, key, value */
271 return 1;
275 * Generic object newindex metamethod handler.
276 * Lua Stack: ud, key, value
278 int object_newindex_handler(lua_State *L)
280 char sskey[16], *skey;
281 TRACE(" newindex(): key='%s' value='%s'\n", lua_tostring(L, 2), lua_tostring(L, 3));
282 if ((!lua_isstring(L, 2)) || (!lua_isstring(L, 3))){
283 gr_object_t *ud = toobject(L, 1, NULL, STRICT);
284 skey = agget(ud->p.p, "__attrib__");
285 if (!skey || (strlen(skey) == 0)){
286 /* Let's create an attrib table on the fly if none exists */
287 sprintf(sskey, "%p", ud->p.p);
288 skey = agstrdup(agroot(ud->p.p), sskey);
289 agset(ud->p.p, "__attrib__", skey);
290 lua_pushstring(L, skey); /* ud, key, value, skey */
291 lua_newtable(L); /* ud, key, value, skey, stab */
292 lua_rawset(L, LUA_REGISTRYINDEX); /* ud, key, value, */
294 lua_pushstring(L, skey); /* ud, key, value, skey */
295 lua_rawget(L, LUA_REGISTRYINDEX); /* ud, key, value, stab */
296 lua_pushvalue(L, 2); /* ud, key, value, stab, key */
297 lua_pushvalue(L, 3); /* ud, key, value, stab, key, value */
298 lua_rawset(L, -3); /* ud, key, value, stab */
299 lua_pop(L, -1); /* ud, key, value */
300 return 0;
302 return setval(L);
306 * Create the graph object with member and method access.
307 * Lua entry stack: ud
308 * Lua exit stack: ud
310 int new_object(lua_State *L, const char *kind, const luaL_Reg *reg_rmembers,
311 const luaL_Reg *reg_methods, const luaL_Reg *reg_metamethods,
312 index_handler_t *index_handler)
314 int methods, metatable;
315 /* Put methods in a table */
316 lua_newtable(L); /* ud, mtab */
318 register_metainfo(L, reg_methods);
319 methods = lua_gettop(L);
321 /* Put metamethods in a new metatable */
322 luaL_newmetatable(L, kind); /* ud, mtab, mt */
324 register_metainfo(L, reg_metamethods);
325 metatable = lua_gettop(L);
327 /* Keep a reference to methods in the new metatable */
328 lua_pushstring(L, "__metatable"); /* ud, mtab, mt, "__metatable" */
329 lua_pushvalue(L, methods); /* ud, mtab, mt, "__metatable", mtab */
330 lua_rawset(L, -3); /* ud, mtab, mt */
332 lua_pushstring(L, "__index"); /* ud, mtab, mt, "__index" */
334 /* First upvalue: metatable => members access */
335 lua_pushvalue(L, metatable); /* ud, mtab, mt, "__index", mt(upv 1) */
337 /* Put members into the metatable */
338 register_metainfo(L, reg_rmembers);
340 /* Second upvalue, methods => methods access */
341 lua_pushvalue(L, methods); /* ud, mtab, mt, "__index", mt(upv 1), mtab(upv 2) */
343 /* Set index_handler with 2 upvalues as __index metamethod */
344 lua_pushcclosure(L, index_handler, 2); /* ud, mtab, mt, "__index", func */
345 lua_rawset(L, metatable); /* ud, mtab, mt */
347 lua_setmetatable(L, -3); /* ud, mtab */
348 lua_pop(L,1); /* ud */
350 return 1;
354 * Check an object type.
356 void *chk_object(lua_State *L, void *obj)
358 gr_object_t *ud = (gr_object_t *) obj;
359 if (ud->p.p == NULL){
360 lua_pushfstring(L, "invalid userdata of type `%s' detected",
361 ud->p.type == AGRAPH ? "graph" :
362 ud->p.type == AGNODE ? "node" :
363 ud->p.type == AGEDGE ? "edge" : "unknown");
364 lua_error(L);
365 return NULL;
367 return obj;
370 /*-------------------------------------------------------------------------*\
371 * Generic method: type = obj.type(self)
372 * Returns type of a graph object.
373 * Returns the type as string: 'graph', 'node' or 'edge'.
374 * Example:
375 * type = n:type()
376 \*-------------------------------------------------------------------------*/
377 int get_object_type(lua_State *L)
379 gr_object_t *ud = toobject(L, 1, NULL, STRICT);
380 lua_pushstring(L, AGTYPE(ud->p.p) == AGRAPH ? "graph" :
381 AGTYPE(ud->p.p) == AGNODE ? "node" :
382 AGTYPE(ud->p.p) == AGEDGE ? "edge" : "unkown");
383 return 1;