1 #include <inttypes.h>
2 #include "mle.h"
3 
4 #define MLE_USCRIPT_KEY "_uscript"
5 
6 #define MLE_USCRIPT_GET(pl, pu) do { \
7     lua_getglobal((pl), MLE_USCRIPT_KEY); \
8     (pu) = luaL_optpointer((pl), -1, NULL); \
9     if (!(pu)) return 0; \
10 } while(0)
11 
12 #define luaL_pushkey(pL, pt, pk, pv) do { \
13     lua_pushstring((pL), (pk)); \
14     (lua_push ## pt) ((pL), (pv)); \
15     lua_settable((pL), -3); \
16 } while (0)
17 
18 #define luaL_pushkey2(pL, pt, pk, pv1, pv2) do { \
19     lua_pushstring((pL), (pk)); \
20     (lua_push ## pt) ((pL), (pv1), (pv2)); \
21     lua_settable((pL), -3); \
22 } while (0)
23 
24 static int _uscript_panic(lua_State *L);
25 static int _uscript_cmd_cb(cmd_context_t *ctx);
26 static int _uscript_observer_cb(char *event_name, void *event_data, void *udata);
27 static int _uscript_write(lua_State *L);
28 static void _uscript_push_event_map(uscript_t *uscript, char *event_name, void *event_data);
29 static void _uscript_push_cmd_map(lua_State *L, cmd_context_t *ctx);
30 static void _uscript_push_baction_map(lua_State *L, baction_t *baction);
31 static int _uscript_func_editor_prompt(lua_State *L);
32 static int _uscript_func_editor_register_cmd(lua_State *L);
33 static int _uscript_func_editor_register_observer(lua_State *L);
34 static int _uscript_func_util_escape_shell_arg(lua_State *L);
35 static int _uscript_func_util_shell_exec(lua_State *L);
36 static int _uscript_func_editor_get_input(lua_State *L);
37 static int _uscript_func_editor_menu(lua_State *L);
38 static int _uscript_func_bview_pop_kmap(lua_State *L);
39 static int _uscript_func_bview_push_kmap(lua_State *L);
40 static int _uscript_func_buffer_set_callback(lua_State *L);
41 static int _uscript_func_buffer_add_srule(lua_State *L);
42 static int _uscript_func_buffer_remove_srule(lua_State *L);
43 static int _uscript_func_buffer_write_to_file(lua_State *L);
44 static int _uscript_func_editor_input_to_key(lua_State *L);
45 static void *luaL_checkpointer(lua_State *L, int arg);
46 static void *luaL_optpointer(lua_State *L, int arg, void *def);
47 static void lua_pushpointer(lua_State *L, void *ptr);
48 static int luaL_checkfunction(lua_State *L, int arg);
49 static int luaopen_mle(lua_State *L);
50 
51 #include "uscript.inc"
52 
53 // Run uscript
uscript_run(editor_t * editor,char * path)54 uscript_t *uscript_run(editor_t *editor, char *path) {
55     lua_State *L;
56     uscript_t *uscript;
57     L = luaL_newstate();
58     luaL_openlibs(L);
59     luaL_requiref(L, "mle", luaopen_mle, 1);
60 
61     uscript = calloc(1, sizeof(uscript_t));
62     uscript->editor = editor;
63     uscript->L = L;
64 
65     lua_pushpointer(L, (void*)uscript);
66     lua_setglobal(L, MLE_USCRIPT_KEY);
67     lua_pop(L, 1);
68 
69     lua_getglobal(L, "_G");
70     lua_pushcfunction(L, _uscript_write);
71     lua_setfield(L, -2, "print");
72     lua_pop(L, 1);
73 
74     lua_atpanic(L, _uscript_panic);
75 
76 
77     luaL_loadfile(L, path); // TODO err
78 
79     lua_pcall(L, 0, 0, 0);
80     return uscript;
81 }
82 
83 // Destroy uscript
uscript_destroy(uscript_t * uscript)84 int uscript_destroy(uscript_t *uscript) {
85     lua_close(uscript->L);
86     return MLE_OK;
87 }
88 
_uscript_panic(lua_State * L)89 static int _uscript_panic(lua_State *L) {
90    MLE_SET_ERR(&_editor, "uscript panic: %s", lua_tostring(L, -1));
91    return 0;
92 }
93 
94 // Invoke cmd in uscript
_uscript_cmd_cb(cmd_context_t * ctx)95 static int _uscript_cmd_cb(cmd_context_t *ctx) {
96     int rv;
97     lua_State *L;
98     uhandle_t *uhandle;
99     int top;
100 
101     uhandle = (uhandle_t*)(ctx->cmd->udata);
102     L = uhandle->uscript->L;
103     top = lua_gettop(L);
104     lua_rawgeti(L, LUA_REGISTRYINDEX, uhandle->callback_ref);
105     _uscript_push_cmd_map(L, ctx);
106 
107     if (lua_pcall(L, 1, 1, 0) != LUA_OK) {
108         printf("err[%s]\n", luaL_checkstring(L, -1));
109         rv = MLE_ERR;
110     } else if (lua_isboolean(L, -1) && !lua_toboolean(L, -1)) {
111         rv = MLE_ERR;
112     } else {
113         rv = MLE_OK;
114     }
115 
116     lua_settop(L, top);
117     return rv;
118 }
119 
_uscript_observer_cb(char * event_name,void * event_data,void * udata)120 static int _uscript_observer_cb(char *event_name, void *event_data, void *udata) {
121     int rv;
122     lua_State *L;
123     uhandle_t *uhandle;
124     uhandle = (uhandle_t*)(udata);
125     L = uhandle->uscript->L;
126 
127     lua_rawgeti(L, LUA_REGISTRYINDEX, uhandle->callback_ref);
128     _uscript_push_event_map(uhandle->uscript, event_name, event_data);
129 
130     if (lua_pcall(L, 1, 1, 0) != LUA_OK) {
131         printf("err[%s]\n", luaL_checkstring(L, -1));
132         rv = MLE_ERR;
133     } else if (lua_isboolean(L, -1) && !lua_toboolean(L, -1)) {
134         rv = MLE_ERR;
135     } else {
136         rv = MLE_OK;
137     }
138 
139     return rv;
140 }
141 
142 // Handle write from uscript
_uscript_write(lua_State * L)143 static int _uscript_write(lua_State *L) {
144     uscript_t *uscript;
145     char *str;
146     mark_t *mark;
147     int i, nargs;
148     nargs = lua_gettop(L);
149     MLE_USCRIPT_GET(L, uscript);
150     if (!uscript->editor->active_edit
151         || !uscript->editor->active_edit->active_cursor
152     ) {
153         return 0;
154     }
155     mark = uscript->editor->active_edit->active_cursor->mark;
156     for (i = 1; i <= nargs; i++) {
157         str = (char*)lua_tostring(L, i);
158         mark_insert_before(mark, str, strlen(str));
159     }
160     return 0;
161 }
162 
_uscript_push_event_map(uscript_t * uscript,char * event_name,void * event_data)163 static void _uscript_push_event_map(uscript_t *uscript, char *event_name, void *event_data) {
164     lua_State *L;
165     L = uscript->L;
166     if (strcmp(event_name, "buffer:baction") == 0) {
167         _uscript_push_baction_map(L, (baction_t*)event_data);
168         return;
169     } else if (strcmp(event_name, "buffer:save") == 0) {
170         lua_createtable(L, 0, 1);
171         luaL_pushkey(L, pointer, "bview", event_data);
172         return;
173     } else if (strncmp(event_name, "cmd:", 4) == 0) {
174         _uscript_push_cmd_map(L, (cmd_context_t*)event_data);
175         return;
176     }
177     lua_pushnil(uscript->L); // TODO
178 }
179 
_uscript_push_cmd_map(lua_State * L,cmd_context_t * ctx)180 static void _uscript_push_cmd_map(lua_State *L, cmd_context_t *ctx) {
181     char input_str[16];
182     editor_input_to_key(ctx->editor, &ctx->input, input_str);
183     lua_createtable(L, 0, 1);
184     luaL_pushkey(L, pointer, "editor",       (void*)ctx->editor);
185     luaL_pushkey(L, pointer, "loop_ctx",     (void*)ctx->loop_ctx);
186     luaL_pushkey(L, pointer, "cmd",          (void*)ctx->cmd);
187     luaL_pushkey(L, pointer, "buffer",       (void*)ctx->buffer);
188     luaL_pushkey(L, pointer, "bview",        (void*)ctx->bview);
189     luaL_pushkey(L, pointer, "cursor",       (void*)ctx->cursor);
190     luaL_pushkey(L, pointer, "mark",         (void*)ctx->cursor->mark);
191     luaL_pushkey(L, string,  "static_param", (const char*)ctx->static_param);
192     luaL_pushkey(L, string,  "input",        (const char*)input_str);
193 }
194 
_uscript_push_baction_map(lua_State * L,baction_t * baction)195 static void _uscript_push_baction_map(lua_State *L, baction_t *baction) {
196     lua_createtable(L, 0, 1);
197     luaL_pushkey(L,  integer, "type",                 baction->type);
198     luaL_pushkey(L,  pointer, "buffer",               (void*)baction->buffer);
199     luaL_pushkey(L,  integer, "start_line_index",     baction->start_line_index);
200     luaL_pushkey(L,  integer, "start_col",            baction->start_col);
201     luaL_pushkey(L,  integer, "maybe_end_line_index", baction->maybe_end_line_index);
202     luaL_pushkey(L,  integer, "maybe_end_col",        baction->maybe_end_col);
203     luaL_pushkey(L,  integer, "byte_delta",           baction->byte_delta);
204     luaL_pushkey(L,  integer, "char_delta",           baction->char_delta);
205     luaL_pushkey(L,  integer, "line_delta",           baction->line_delta);
206     luaL_pushkey2(L, lstring, "data",                 (const char*)baction->data, baction->data_len);
207 }
208 
209 // foreign static string _uscript_func_editor_prompt(prompt)
_uscript_func_editor_prompt(lua_State * L)210 static int _uscript_func_editor_prompt(lua_State *L) {
211     uscript_t *uscript;
212     char *prompt;
213     char *answer = NULL;
214     MLE_USCRIPT_GET(L, uscript);
215     prompt = (char*)luaL_checkstring(L, 1);
216     if (editor_prompt(uscript->editor, prompt, NULL, &answer) == MLE_OK && answer) {
217         lua_pushstring(L, answer);
218         free(answer);
219     } else {
220         lua_pushnil(L);
221     }
222     return 1;
223 }
224 
225 int editor_register_cmd(editor_t *editor, cmd_t *cmd);
226 
227 // foreign static int _uscript_func_editor_register_cmd(cmd_name, fn_callback)
_uscript_func_editor_register_cmd(lua_State * L)228 static int _uscript_func_editor_register_cmd(lua_State *L) {
229     uscript_t *uscript;
230     uhandle_t *uhandle;
231     int rv;
232     char *cmd_name;
233     int fn_callback;
234     cmd_t cmd = {0};
235     MLE_USCRIPT_GET(L, uscript);
236 
237     cmd_name = (char*)luaL_checkstring(L, 1); // strdup'd by editor_register_cmd
238     fn_callback = luaL_checkfunction(L, 2);
239 
240     uhandle = calloc(1, sizeof(uhandle_t));
241     uhandle->uscript = uscript;
242     uhandle->callback_ref = fn_callback;
243     DL_APPEND(uscript->uhandles, uhandle);
244 
245     cmd.name = cmd_name;
246     cmd.func = _uscript_cmd_cb;
247     cmd.udata = (void*)uhandle;
248     rv = editor_register_cmd(uscript->editor, &cmd);
249 
250     lua_createtable(L, 0, 1);
251     luaL_pushkey(L, integer, "rv", rv);
252     lua_pushvalue(L, -1);
253     return 1;
254 }
255 
256 // foreign static int _uscript_func_editor_register_observer(event_name, fn_callback)
_uscript_func_editor_register_observer(lua_State * L)257 static int _uscript_func_editor_register_observer(lua_State *L) {
258     int rv;
259     char *event_name;
260     int fn_callback;
261     uscript_t *uscript;
262     uhandle_t *uhandle;
263     MLE_USCRIPT_GET(L, uscript);
264 
265     event_name = (char*)luaL_checkstring(L, 1);
266     fn_callback = luaL_checkfunction(L, 2);
267 
268     uhandle = calloc(1, sizeof(uhandle_t));
269     uhandle->uscript = uscript;
270     uhandle->callback_ref = fn_callback;
271     DL_APPEND(uscript->uhandles, uhandle);
272 
273     rv = editor_register_observer(uscript->editor, event_name, (void*)uhandle, _uscript_observer_cb, NULL);
274 
275     lua_createtable(L, 0, 1);
276     luaL_pushkey(L, integer, "rv", rv);
277     lua_pushvalue(L, -1);
278     return 1;
279 }
280 
281 // foreign static int _uscript_func_util_escape_shell_arg(arg)
_uscript_func_util_escape_shell_arg(lua_State * L)282 static int _uscript_func_util_escape_shell_arg(lua_State *L) {
283     (void)L;
284     return 0;
285 }
286 
287 // foreign static int _uscript_func_util_shell_exec(cmd, timeout_s)
_uscript_func_util_shell_exec(lua_State * L)288 static int _uscript_func_util_shell_exec(lua_State *L) {
289     int rv;
290     char *cmd;
291     long timeout_s;
292     uscript_t *uscript;
293     char *output;
294     size_t output_len;
295     MLE_USCRIPT_GET(L, uscript);
296 
297     cmd = (char*)luaL_checkstring(L, 1);
298     timeout_s = (long)luaL_checkinteger(L, 2);
299     output = NULL;
300     output_len = 0;
301     rv = util_shell_exec(uscript->editor, cmd, timeout_s, NULL, 0, 0, NULL, &output, &output_len);
302 
303     lua_createtable(L, 0, 1);
304     luaL_pushkey(L, integer, "rv", rv);
305     luaL_pushkey2(L, lstring, "output", (output ? output : ""), output_len);
306     lua_pushvalue(L, -1);
307     if (output) free(output);
308     return 1;
309 }
310 
311 // foreign static int _uscript_func_editor_get_input(x, y, z)
_uscript_func_editor_get_input(lua_State * L)312 static int _uscript_func_editor_get_input(lua_State *L) {
313     // TODO
314     (void)L;
315     return 0;
316 }
317 
318 // foreign static int _uscript_func_editor_menu(x, y, z)
_uscript_func_editor_menu(lua_State * L)319 static int _uscript_func_editor_menu(lua_State *L) {
320     // TODO
321     (void)L;
322     return 0;
323 }
324 
325 // foreign static int _uscript_func_bview_pop_kmap(x, y, z)
_uscript_func_bview_pop_kmap(lua_State * L)326 static int _uscript_func_bview_pop_kmap(lua_State *L) {
327     // TODO
328     (void)L;
329     return 0;
330 }
331 
332 // foreign static int _uscript_func_bview_push_kmap(x, y, z)
_uscript_func_bview_push_kmap(lua_State * L)333 static int _uscript_func_bview_push_kmap(lua_State *L) {
334     // TODO
335     (void)L;
336     return 0;
337 }
338 
339 // foreign static int _uscript_func_buffer_set_callback(x, y, z)
_uscript_func_buffer_set_callback(lua_State * L)340 static int _uscript_func_buffer_set_callback(lua_State *L) {
341     // TODO
342     (void)L;
343     return 0;
344 }
345 
346 // foreign static int _uscript_func_buffer_add_srule(x, y, z)
_uscript_func_buffer_add_srule(lua_State * L)347 static int _uscript_func_buffer_add_srule(lua_State *L) {
348     // TODO
349     (void)L;
350     return 0;
351 }
352 
353 // foreign static int _uscript_func_buffer_remove_srule(x, y, z)
_uscript_func_buffer_remove_srule(lua_State * L)354 static int _uscript_func_buffer_remove_srule(lua_State *L) {
355     // TODO
356     (void)L;
357     return 0;
358 }
359 
360 // foreign static int _uscript_func_buffer_write_to_file(x, y, z)
_uscript_func_buffer_write_to_file(lua_State * L)361 static int _uscript_func_buffer_write_to_file(lua_State *L) {
362     // TODO
363     (void)L;
364     return 0;
365 }
366 
367 // foreign static int _uscript_func_editor_input_to_key(x, y, z)
_uscript_func_editor_input_to_key(lua_State * L)368 static int _uscript_func_editor_input_to_key(lua_State *L) {
369     // TODO
370     (void)L;
371     return 0;
372 }
373 
luaL_checkpointer(lua_State * L,int arg)374 static void *luaL_checkpointer(lua_State *L, int arg) {
375     luaL_checktype(L, arg, LUA_TSTRING);
376     return luaL_optpointer(L, arg, NULL);
377 }
378 
luaL_optpointer(lua_State * L,int arg,void * def)379 static void *luaL_optpointer(lua_State *L, int arg, void *def) {
380     const char *ptr;
381     uintptr_t ptr_as_int;
382     ptr = luaL_optstring(L, arg, NULL);
383     if (ptr && strlen(ptr) > 0) {
384         ptr_as_int = (uintptr_t)strtoull(ptr, NULL, 16);
385         return (void*)ptr_as_int;
386     }
387     return def;
388 }
389 
lua_pushpointer(lua_State * L,void * ptr)390 static void lua_pushpointer(lua_State *L, void *ptr) {
391     char ptrbuf[32];
392     if (ptr == NULL) {
393         lua_pushnil(L);
394     } else {
395         snprintf(ptrbuf, 32, "%" PRIxPTR, (uintptr_t)ptr);
396         lua_pushstring(L, ptrbuf);
397     }
398 }
399 
luaL_checkfunction(lua_State * L,int arg)400 static int luaL_checkfunction(lua_State *L, int arg) {
401     luaL_checktype(L, arg, LUA_TFUNCTION);
402     lua_pushvalue(L, arg);
403     return luaL_ref(L, LUA_REGISTRYINDEX);
404 }
405 
luaopen_mle(lua_State * L)406 static int luaopen_mle(lua_State *L) {
407     luaL_newlib(L, mle_lib);
408     return 1;
409 }
410