1 /*
2 * peas-lua-utils.c
3 * This file is part of libpeas
4 *
5 * Copyright (C) 2014-2015 - Garrett Regier
6 *
7 * libpeas is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * libpeas is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include "peas-lua-utils.h"
27
28 #include <gio/gio.h>
29
30 #include <lauxlib.h>
31
32
33 gboolean
peas_lua_utils_require(lua_State * L,const gchar * name)34 peas_lua_utils_require (lua_State *L,
35 const gchar *name)
36 {
37 luaL_checkstack (L, 2, "");
38
39 lua_getglobal (L, "require");
40 lua_pushstring (L, name);
41
42 if (lua_pcall (L, 1, 1, 0) != 0)
43 {
44 g_warning ("Error failed to load Lua module '%s': %s",
45 name, lua_tostring (L, -1));
46
47 /* Pop error */
48 lua_pop (L, 1);
49 return FALSE;
50 }
51
52 if (!lua_istable (L, -1))
53 {
54 g_warning ("Error invalid Lua module for '%s': "
55 "expected table, got: %s",
56 name, lua_tostring (L, -1));
57
58 /* Pop the module's table */
59 lua_pop (L, 1);
60 return FALSE;
61 }
62
63 return TRUE;
64 }
65
66 gboolean
peas_lua_utils_check_version(lua_State * L,guint req_major,guint req_minor,guint req_micro)67 peas_lua_utils_check_version (lua_State *L,
68 guint req_major,
69 guint req_minor,
70 guint req_micro)
71 {
72 const gchar *version_str;
73 gchar **version_str_parts;
74 gint n_version_parts;
75 gint64 *version_parts;
76 gint i;
77 gboolean success = FALSE;
78
79 lua_getfield (L, -1, "_VERSION");
80 version_str = lua_tostring (L, -1);
81
82 version_str_parts = g_strsplit (version_str, ".", 0);
83
84 n_version_parts = g_strv_length (version_str_parts);
85 version_parts = g_newa (gint64, n_version_parts);
86
87 for (i = 0; i < n_version_parts; ++i)
88 {
89 gchar *end;
90
91 version_parts[i] = g_ascii_strtoll (version_str_parts[i], &end, 10);
92
93 if (*end != '\0' ||
94 version_parts[i] < 0 ||
95 version_parts[i] == G_MAXINT64)
96 {
97 g_warning ("Invalid version string: %s", version_str);
98 goto error;
99 }
100 }
101
102 if (n_version_parts < 3 ||
103 version_parts[0] != req_major ||
104 version_parts[1] < req_minor ||
105 (version_parts[1] == req_minor && version_parts[2] < req_micro))
106 {
107 g_warning ("Version mismatch %d.%d.%d is required, found %s",
108 req_major, req_minor, req_micro, version_str);
109 goto error;
110 }
111
112 success = TRUE;
113
114 error:
115
116 /* Pop _VERSION */
117 lua_pop (L, 1);
118
119 g_strfreev (version_str_parts);
120 return success;
121 }
122
123 static gint
traceback(lua_State * L)124 traceback (lua_State *L)
125 {
126 /* Always ignore an error that isn't a string */
127 if (!lua_isstring (L, 1))
128 return 1;
129
130 lua_getglobal (L, "debug");
131 if (!lua_istable (L, -1))
132 {
133 lua_pop (L, 1);
134 return 1;
135 }
136
137 lua_getfield (L, -1, "traceback");
138 if (!lua_isfunction (L, -1))
139 {
140 lua_pop (L, 2);
141 return 1;
142 }
143
144 /* Replace debug with traceback */
145 lua_replace (L, -2);
146
147 /* Push the error */
148 lua_pushvalue (L, 1);
149
150 /* Skip this function when generating the traceback */
151 lua_pushinteger (L, 2);
152
153 /* If we fail we have a new error object... */
154 lua_pcall (L, 2, 1, 0);
155 return 1;
156 }
157
158 gboolean
peas_lua_utils_call(lua_State * L,guint n_args,guint n_results)159 peas_lua_utils_call (lua_State *L,
160 guint n_args,
161 guint n_results)
162 {
163 gboolean success;
164
165 /* Push the error function */
166 lua_pushcfunction (L, traceback);
167
168 /* Move traceback to before the arguments */
169 lua_insert (L, -2 - n_args);
170
171 success = lua_pcall (L, n_args, n_results, -2 - n_args) == 0;
172
173 /* Remove traceback */
174 lua_remove (L, -1 - (success ? n_results : 1));
175 return success;
176 }
177
178 gboolean
peas_lua_utils_load_resource(lua_State * L,const gchar * name,guint n_args,guint n_results)179 peas_lua_utils_load_resource (lua_State *L,
180 const gchar *name,
181 guint n_args,
182 guint n_results)
183 {
184 gchar *resource_path;
185 GBytes *lua_resource;
186 const gchar *code;
187 gsize code_len;
188 gchar *lua_filename;
189
190 /* We don't use the byte-compiled Lua source
191 * because glib-compile-resources cannot output
192 * depends for generated files.
193 *
194 * There are also concerns that the bytecode is
195 * not stable enough between different Lua versions.
196 *
197 * https://bugzilla.gnome.org/show_bug.cgi?id=673101
198 */
199 resource_path = g_strconcat ("/org/gnome/libpeas/loaders/lua5.1/",
200 name, NULL);
201 lua_resource = g_resources_lookup_data (resource_path,
202 G_RESOURCE_LOOKUP_FLAGS_NONE,
203 NULL);
204 g_free (resource_path);
205
206 if (lua_resource == NULL)
207 {
208 g_warning ("Failed to find '%s' resource", name);
209 return FALSE;
210 }
211
212 code = g_bytes_get_data (lua_resource, &code_len);
213
214 /* Filenames are prefixed with '@' */
215 lua_filename = g_strconcat ("@peas-lua-", name, NULL);
216
217 if (luaL_loadbuffer (L, code, code_len, lua_filename) != 0)
218 {
219 g_warning ("Failed to load '%s' resource: %s",
220 name, lua_tostring (L, -1));
221
222 /* Pop error */
223 lua_pop (L, 1);
224 g_free (lua_filename);
225 g_bytes_unref (lua_resource);
226 return FALSE;
227 }
228
229 g_free (lua_filename);
230 g_bytes_unref (lua_resource);
231
232 if (!peas_lua_utils_call (L, n_args, n_results))
233 {
234 g_warning ("Failed to run '%s' resource: %s",
235 name, lua_tostring (L, -1));
236
237 /* Pop error */
238 lua_pop (L, 1);
239 return FALSE;
240 }
241
242 return TRUE;
243 }
244