1 /*
2  * glspi_kfile.c - This file is part of the Lua scripting plugin for the Geany IDE
3  * See the file "geanylua.c" for copyright information.
4  */
5 
6 /* Minimal interface to a GKeyFile object */
7 
8 #include <glib.h>
9 #include <glib/gi18n.h>
10 
11 #include <lua.h>
12 #include <lualib.h>
13 #include <lauxlib.h>
14 
15 #define LUA_MODULE_NAME "keyfile"
16 #define MetaName "_g_key_file_metatable"
17 
18 
19 #define SetTableValue(name,value,pusher) \
20 	lua_pushstring(L, name); \
21 	pusher(L, value); \
22 	lua_rawset(L,-3);
23 
24 #define SetTableStr(name,value) SetTableValue(name,value,lua_pushstring)
25 
26 #define FAIL_STRING_ARG(argnum) \
27 	(fail_arg_type(L,__FUNCTION__,argnum,"string"))
28 
29 #define FAIL_BOOL_ARG(argnum) \
30 	(fail_arg_type(L,__FUNCTION__,argnum,"boolean"))
31 
32 #define FAIL_TABLE_ARG(argnum) \
33 	(fail_arg_type(L,__FUNCTION__,argnum,"table"))
34 
35 #define FAIL_KEYFILE_ARG(argnum) \
36 	(fail_arg_type(L,__FUNCTION__,argnum,LuaKeyFileType))
37 
38 
39 #define push_number(L,n) lua_pushnumber(L,(lua_Number)n)
40 
41 /* Subtract one from error argnum if we have implicit "self" */
adjust_argnum(lua_State * L,gint argnum)42 static gint adjust_argnum(lua_State *L, gint argnum) {
43 	lua_Debug ar;
44 	if (lua_getstack(L, 0, &ar)){
45 		lua_getinfo(L, "n", &ar);
46 		if (g_str_equal(ar.namewhat, "method")) { return argnum-1; }
47 	}
48 	return argnum;
49 }
50 
51 /* Pushes an error message onto Lua stack if script passes a wrong arg type */
fail_arg_type(lua_State * L,const gchar * func,gint argnum,const gchar * type)52 static gint fail_arg_type(lua_State *L, const gchar *func, gint argnum, const gchar *type)
53 {
54 	lua_pushfstring(L, _("Error in module \"%s\" at function %s():\n"
55 											" expected type \"%s\" for argument #%d\n"),
56 											LUA_MODULE_NAME, func+6, type, adjust_argnum(L,argnum));
57 	lua_error(L);
58 	return 0;
59 }
60 
61 
62 #define KF_FLAGS G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS
63 
64 static const gchar*LuaKeyFileType="GKeyFile";
65 
66 typedef struct _LuaKeyFile
67 {
68 	const gchar*id;
69 	GKeyFile*kf;
70 	gboolean managed;
71 } LuaKeyFile;
72 
73 
74 typedef gint (*KeyfileAssignFunc) (lua_State *L, GKeyFile*kf);
75 
glspi_kfile_assign(lua_State * L,GKeyFile * kf)76 static gint glspi_kfile_assign(lua_State *L, GKeyFile*kf)
77 {
78 	LuaKeyFile*k=(LuaKeyFile*)lua_newuserdata(L,sizeof(LuaKeyFile));
79 	k->id=LuaKeyFileType;
80 	k->kf=kf;
81 	luaL_getmetatable(L, MetaName);
82 	lua_setmetatable(L, -2);
83 	k->managed=FALSE;
84 	return 1;
85 }
86 
87 
88 
kfile_new(lua_State * L)89 static gint kfile_new(lua_State *L)
90 {
91 	LuaKeyFile*k=(LuaKeyFile*)lua_newuserdata(L,sizeof(LuaKeyFile));
92 	k->id=LuaKeyFileType;
93 	k->kf=g_key_file_new();
94 	luaL_getmetatable(L, MetaName);
95 	lua_setmetatable(L, -2);
96 	k->managed=TRUE;
97 	return 1;
98 }
99 
100 
101 
kfile_done(lua_State * L)102 static gint kfile_done(lua_State *L)
103 {
104 	LuaKeyFile*k;
105 	if (lua_isnil(L, 1)) { return 0; }
106 	k=(LuaKeyFile*)lua_touserdata(L,1);
107 	if ( (k->id!=LuaKeyFileType) || (!k->managed) ) { return 1; }
108 	g_key_file_free(k->kf);
109 	return 1;
110 }
111 
112 
113 
tokeyfile(lua_State * L,gint argnum)114 static LuaKeyFile* tokeyfile(lua_State *L, gint argnum)
115 {
116 	LuaKeyFile* rv;
117 	if ( (lua_gettop(L)<argnum) || (!lua_isuserdata(L,argnum))) return NULL;
118 	rv=lua_touserdata(L,argnum);
119 	return (rv && (rv->id==LuaKeyFileType))?rv:NULL;
120 }
121 
122 
123 
kfile_data(lua_State * L)124 static gint kfile_data(lua_State *L)
125 {
126 	LuaKeyFile*k;
127 
128 	gsize len=0;
129 	GError *err=NULL;
130 
131 	if (lua_gettop(L)>1) {
132 		const gchar *data=NULL;
133 		if ((lua_gettop(L)<2)||(!lua_isstring(L,2))) {return FAIL_STRING_ARG(2); }
134 		data=lua_tolstring(L,2,&len);
135 		k=tokeyfile(L,1);
136 		if (!k) {return FAIL_KEYFILE_ARG(1); }
137 		g_key_file_load_from_data(k->kf, data, len, KF_FLAGS, &err);
138 		if (err) {
139 			lua_pushstring(L,err->message);
140 			g_error_free(err);
141 		} else {lua_pushnil(L);}
142 		return 1;
143 	} else {
144 		gchar *data=NULL;
145 		k=tokeyfile(L,1);
146 		if (!k) {return FAIL_KEYFILE_ARG(1); }
147 		data=g_key_file_to_data(k->kf,&len,&err);
148 		if (!err) {
149 			lua_pushlstring(L,data,len);
150 			g_free(data);
151 			return 1;
152 		} else {
153 			lua_pushnil(L);
154 			lua_pushstring(L,err->message);
155 			g_error_free(err);
156 			if (data) {g_free(data);}
157 			return 2;
158 		}
159 	}
160 }
161 
162 
163 
164 /*
165 	Lua "closure" function to iterate through each string in an array of strings
166 */
strings_closure(lua_State * L)167 static gint strings_closure(lua_State *L)
168 {
169 	gchar **strings;
170 	gchar *string;
171 	gint i=lua_tonumber(L, lua_upvalueindex(2));
172 	strings=lua_touserdata(L,lua_upvalueindex(1));
173 	if (!strings) {return 0;}
174 	string=strings[i];
175 	if ( string ) {
176 		lua_pushstring(L,string);
177 		push_number(L, i+1);
178 		lua_pushvalue(L, -1);
179 		lua_replace(L, lua_upvalueindex(2));
180 		return 2;
181 	} else {
182 		g_strfreev(strings);
183 		return 0;
184 	}
185 }
186 
187 
188 
189 /* Iterate through the group names of a keyfile */
kfile_groups(lua_State * L)190 static gint kfile_groups(lua_State* L)
191 {
192 	LuaKeyFile*k=NULL;
193 	gchar**groups=NULL;
194 	gsize len=0;
195 	k=tokeyfile(L,1);
196 	if (!k) {return FAIL_KEYFILE_ARG(1); }
197 	groups=g_key_file_get_groups(k->kf, &len);
198 	lua_pushlightuserdata(L,groups);
199 	push_number(L,0);
200 	lua_pushcclosure(L, &strings_closure, 2);
201 	return 1;
202 }
203 
204 
205 
206 /* Iterate through the key names of a group*/
kfile_keys(lua_State * L)207 static gint kfile_keys(lua_State* L)
208 {
209 	LuaKeyFile*k=NULL;
210 	gchar**keylist=NULL;
211 	gsize len=0;
212 	const gchar*group=NULL;
213 	GError *err=NULL;
214 	if ((lua_gettop(L)<2)||(!lua_isstring(L,2))) {return FAIL_STRING_ARG(2); }
215 	group=lua_tostring(L,2);
216 	k=tokeyfile(L,1);
217 	if (!k) {return FAIL_KEYFILE_ARG(1); }
218 	keylist=g_key_file_get_keys(k->kf, group, &len,  &err);
219 	if (err) {
220 		g_error_free(err);
221 	}
222 	lua_pushlightuserdata(L,keylist);
223 	push_number(L,0);
224 	lua_pushcclosure(L, &strings_closure, 2);
225 	return 1;
226 }
227 
228 
229 
kfile_value(lua_State * L)230 static gint kfile_value(lua_State* L)
231 {
232 	const gchar *group=NULL;
233 	const gchar *key=NULL;
234 	const gchar *value=NULL;
235 	LuaKeyFile*k=NULL;
236 	GError *err=NULL;
237 	if (lua_gettop(L)>=4) {
238 		if (!lua_isstring(L,4)) {return FAIL_STRING_ARG(4);}
239 		value=lua_tostring(L,4);
240 	}
241 	if ((lua_gettop(L)<3)||(!lua_isstring(L,3))) {return FAIL_STRING_ARG(3); }
242 	key=lua_tostring(L,3);
243 	if (!lua_isstring(L,2)) {return FAIL_STRING_ARG(2); }
244 	group=lua_tostring(L,2);
245 	k=tokeyfile(L,1);
246 	if (!k) {return FAIL_KEYFILE_ARG(1); }
247 
248 	if (value) {
249 		g_key_file_set_value(k->kf,group,key,value);
250 		return 0;
251 	} else {
252 		gchar *kfv=g_key_file_get_value(k->kf,group,key,&err);
253 		if (err) {
254 			g_error_free(err);
255 		}
256 		if (kfv) {
257 			lua_pushstring(L, kfv);
258 			g_free(kfv);
259 			return 1;
260 		} else {
261 			return 0;
262 		}
263 	}
264 }
265 
266 
267 
268 #define str_or_nil(L,argnum) (lua_isstring(L,argnum)||lua_isnil(L,argnum))
269 
270 
kfile_comment(lua_State * L)271 static gint kfile_comment(lua_State* L)
272 {
273 	const gchar *group=NULL;
274 	const gchar *key=NULL;
275 	const gchar *comment=NULL;
276 	LuaKeyFile*k=NULL;
277 	GError *err=NULL;
278 	if (lua_gettop(L)>=4) {
279 		if (!lua_isstring(L,4)) {return FAIL_STRING_ARG(4);}
280 		comment=lua_tostring(L,4);
281 	}
282 	if ((lua_gettop(L)<3)||(!str_or_nil(L,3))) {return FAIL_STRING_ARG(3); }
283 	key=lua_tostring(L,3);
284 	if (!str_or_nil(L,2)) {return FAIL_STRING_ARG(2); }
285 	group=lua_tostring(L,2);
286 	k=tokeyfile(L,1);
287 	if (!k) {return FAIL_KEYFILE_ARG(1); }
288 
289 	if (comment) {
290 		g_key_file_set_comment(k->kf,group,key,comment,&err);
291 		return 0;
292 	} else {
293 		gchar*kfc=g_key_file_get_comment(k->kf,group,key,&err);
294 		if (err) {
295 			g_error_free(err);
296 		}
297 		if (kfc) {
298 			lua_pushstring(L, kfc);
299 			g_free(kfc);
300 			return 1;
301 		} else {
302 			return 0;
303 		}
304 	}
305 }
306 
307 
308 
kfile_has(lua_State * L)309 static gint kfile_has(lua_State* L)
310 {
311 	const gchar *group=NULL;
312 	const gchar *key=NULL;
313 	gint argc=lua_gettop(L);
314 	gboolean hasit=FALSE;
315 	LuaKeyFile*k=NULL;
316 	GError*err=NULL;
317 	if (argc>=3) {
318 		if (lua_isstring(L,3)) {
319 			key=lua_tostring(L,3);
320 		} else {
321 			if (!lua_isnil(L,3)) { return FAIL_STRING_ARG(3); }
322 		}
323 	}
324 	if ((lua_gettop(L)<2)||(!lua_isstring(L,2))) { return FAIL_STRING_ARG(2); }
325 	group=lua_tostring(L,2);
326 	k=tokeyfile(L,1);
327 	if (!k) { return FAIL_KEYFILE_ARG(1); }
328 	if (key) {
329 		hasit=g_key_file_has_key(k->kf, group, key, &err);
330 	} else {
331 		hasit=g_key_file_has_group(k->kf, group);
332 	}
333 	lua_pushboolean(L,hasit);
334 	if (err) {
335 		g_error_free(err);
336 	}
337 	return 1;
338 }
339 
340 
341 
kfile_remove(lua_State * L)342 static gint kfile_remove(lua_State* L)
343 {
344 	const gchar *group=NULL;
345 	const gchar *key=NULL;
346 	gint argc=lua_gettop(L);
347 	LuaKeyFile*k=NULL;
348 	GError*err=NULL;
349 	if (argc>=3) {
350 		if (lua_isstring(L,3)) {
351 			key=lua_tostring(L,3);
352 		} else {
353 			if (!lua_isnil(L,3)) { return FAIL_STRING_ARG(3); }
354 		}
355 	}
356 	if ((lua_gettop(L)<2)||(!lua_isstring(L,2))) { return FAIL_STRING_ARG(2); }
357 	group=lua_tostring(L,3);
358 	k=tokeyfile(L,1);
359 	if (!k) { return FAIL_KEYFILE_ARG(1); }
360 	if (key) {
361 		g_key_file_remove_key(k->kf, group, key, &err);
362 	} else {
363 		g_key_file_remove_group(k->kf, group, &err);
364 	}
365 	if (err) {
366 		g_error_free(err);
367 	}
368 	return 0;
369 }
370 
371 
372 
373 static const struct luaL_reg kfile_funcs[] = {
374 	{"new",     kfile_new},
375 	{"data",    kfile_data},
376 	{"groups",  kfile_groups},
377 	{"keys",    kfile_keys},
378 	{"value",   kfile_value},
379 	{"comment", kfile_comment},
380 	{"has",     kfile_has},
381 	{"remove",  kfile_remove},
382 	{NULL,NULL}
383 };
384 
385 
386 
387 
luaopen_keyfile(lua_State * L)388 static gint luaopen_keyfile(lua_State *L)
389 {
390 	luaL_newmetatable(L, MetaName);
391 	lua_pushstring(L, "__index");
392 	lua_pushvalue(L, -2);
393 	lua_settable(L, -3);
394 	luaL_getmetatable(L, MetaName);
395 	lua_pushstring(L,"__gc");
396 	lua_pushcfunction(L,kfile_done);
397 	lua_rawset(L,-3);
398 	luaL_openlib(L, NULL, &kfile_funcs[1], 0);
399 	luaL_openlib(L, LUA_MODULE_NAME, kfile_funcs, 0);
400 	return 0;
401 }
402 
403 
404 
405 
406 
glspi_init_kfile_module(lua_State * L,KeyfileAssignFunc * func)407 void glspi_init_kfile_module(lua_State *L, KeyfileAssignFunc *func)
408 {
409 
410 *func=glspi_kfile_assign;
411 	luaopen_keyfile(L);
412 }
413 
414 
415 
416