]> git.neil.brown.name Git - edlib.git/blob - core-editor.c
discard old auto-load code
[edlib.git] / core-editor.c
1 /*
2  * Copyright Neil Brown ©2015-2023 <neil@brown.name>
3  * May be distributed under terms of GPLv2 - see file:COPYING
4  */
5 #define _GNU_SOURCE
6 #include <unistd.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <stdio.h>
10 #include <stdarg.h>
11 #include <dlfcn.h>
12
13 #include "core.h"
14 #include "misc.h"
15 #include "internal.h"
16
17 #define ED_MAGIC 0x4321fedcUL
18
19 static struct map *ed_map safe;
20 struct ed_info {
21         unsigned long magic;
22         struct pane *freelist;
23         struct mark *mark_free_list;
24         struct map *map safe;
25         struct lookup_cmd cmd;
26         bool testing;
27         struct store {
28                 struct store *next;
29                 int size;
30                 char space[];
31         } *store;
32 };
33
34 bool edlib_testing(struct pane *p safe)
35 {
36         struct ed_info *ei = pane_root(p)->data;
37         return ei->testing;
38 }
39
40 DEF_LOOKUP_CMD(ed_handle, ed_map);
41
42 DEF_CMD(global_set_attr)
43 {
44         char *v;
45         if (!ci->str)
46                 return Enoarg;
47         if (!ci->num) {
48                 attr_set_str(&ci->home->attrs, ci->str, ci->str2);
49                 return 1;
50         }
51         /* Append */
52         if (!ci->str2)
53                 return 1;
54         v = attr_find(ci->home->attrs, ci->str);
55         if (!v) {
56                 attr_set_str(&ci->home->attrs, ci->str, ci->str2);
57                 return 1;
58         }
59         v = strconcat(ci->home, v, ci->str2);
60         attr_set_str(&ci->home->attrs, ci->str, v);
61         return 1;
62 }
63
64 DEF_CMD(global_set_command)
65 {
66         struct ed_info *ei = ci->home->data;
67         struct map *map = ei->map;
68         bool prefix = strcmp(ci->key, "global-set-command-prefix") == 0;
69
70         if (!ci->str)
71                 return Enoarg;
72         if (prefix) {
73                 char *e = strconcat(NULL, ci->str, "\xFF\xFF\xFF\xFF");
74                 key_add_range(map, ci->str, e, ci->comm2);
75                 free(e);
76         } else if (ci->str2)
77                 key_add_range(map, ci->str, ci->str2, ci->comm2);
78         else
79                 key_add(map, ci->str, ci->comm2);
80         return 1;
81 }
82
83 DEF_CMD(global_get_command)
84 {
85         struct ed_info *ei = ci->home->data;
86         struct map *map = ei->map;
87         struct command *cm;
88
89         if (!ci->str ||
90             !(cm = key_lookup_cmd(map, ci->str)))
91                 return Efail;
92         return comm_call(ci->comm2, "callback:comm", ci->focus,
93                          0, NULL, ci->str,
94                          0, NULL, NULL, 0,0, cm);
95 }
96
97 DEF_CMD(global_config_dir)
98 {
99         const char *var = ci->str;
100         char *dir; // ci->str2;
101         char *key, *val = NULL;
102         struct pane *p = ci->home;
103         char *end;
104
105         /* var might be different in different directories.
106          * Config setting are attributes stored on root that
107          * look like "config:var:dir".
108          * We find the best and return that with the dir
109          */
110         if (!var || !ci->str2 || !ci->comm2)
111                 return Enoarg;
112         key = strconcat(p, "config:", var, ":", ci->str2);
113         dir = key + 7 + strlen(var) + 1;
114         end = dir + strlen(dir);
115         while (!val && end > dir) {
116                 end[0] = 0;
117                 val = attr_find(p->attrs, key);
118                 if (end[-1] == '/') {
119                         while (end > dir && end[-1] == '/')
120                                 end -= 1;
121                 } else {
122                         while (end > dir && end[-1] != '/')
123                                 end -= 1;
124                 }
125         }
126         if (!val)
127                 return Efalse;
128         comm_call(ci->comm2, "cb", ci->focus, 0, NULL, val,
129                           0, NULL, dir);
130         return 1;
131 }
132
133 #ifdef edlib_init
134 #include "O/mod-list-decl.h"
135 typedef void init_func(struct pane *ed);
136 static struct builtin {
137         char *name;
138         init_func *func;
139 } builtins[]={
140         #include "O/mod-list.h"
141 };
142 #endif
143 DEF_CMD(editor_load_module)
144 {
145         struct ed_info *ei = ci->home->data;
146         struct map *map = ei->map;
147         const char *name = ci->str;
148         char buf[PATH_MAX];
149 #ifndef edlib_init
150         void *h;
151         void (*s)(struct pane *p);
152         char **path;
153         char pbuf[PATH_MAX];
154
155         sprintf(buf, "edlib-%s.so", name);
156         /* RTLD_GLOBAL is needed for python, else we get
157          * errors about _Py_ZeroStruct which a python script
158          * tries "import gtk"
159          *
160          */
161         h = dlopen(buf, RTLD_NOW | RTLD_GLOBAL);
162         if (h) {
163                 path = dlsym(h, "edlib_module_path");
164                 if (path) {
165                         if (dlinfo(h, RTLD_DI_ORIGIN, pbuf) == 0)
166                                 *path = pbuf;
167                 }
168                 s = dlsym(h, "edlib_init");
169                 if (s) {
170                         char *v = dlsym(h, "edlib_version");
171                         LOG("Loading %s - version %s", name, v ?: "not provided");
172                         s(ci->home);
173                         if (path)
174                                 *path = NULL;
175                         return 1;
176                 }
177         } else {
178                 char *err = dlerror();
179                 if (strstr(err, "No such file or directory") == NULL)
180                         LOG("dlopen %s failed %s", buf, err);
181         }
182 #else
183         unsigned int i;
184
185         strcpy(buf, name);
186         for (i = 0; buf[i]; i++)
187                 if (buf[i] == '-')
188                         buf[i] = '_';
189         for (i = 0; i < sizeof(builtins)/sizeof(builtins[0]); i++)
190                 if (strcmp(builtins[i].name, buf) == 0) {
191                         builtins[i].func(ci->home);
192                         return 1;
193                 }
194 #endif
195         if (key_lookup_prefix(map, ci) > 0)
196                 return 1;
197         LOG("Failed to load module: %s", name);
198         return Efail;
199 }
200
201 DEF_CMD(editor_auto_event)
202 {
203         /* Event handlers register under a private name so we
204          * have to use key_lookup_prefix to find them.
205          * If nothing is found, autoload lib-libevent (hack?)
206          */
207         struct ed_info *ei = ci->home->data;
208         struct map *map = ei->map;
209         int ret = key_lookup_prefix(map, ci);
210
211         if (ret)
212                 return ret;
213         if (strcmp(ci->key, "event:refresh") == 0)
214                 /* pointless to autoload for refresh */
215                 return Efallthrough;
216         call("attach-libevent", ci->home);
217         return key_lookup_prefix(map, ci);
218 }
219
220 static const char *initial_panes[] = {
221         "attach-x11selection",
222         "attach-messageline",
223         "attach-global-keymap",
224         "attach-mode-emacs",
225         "attach-tile",
226         NULL
227 };
228
229 DEF_CMD(editor_activate_display)
230 {
231         /* Given a display attached to the root, integrate it
232          * into a full initial stack of panes.
233          */
234         struct pane *disp = ci->focus;
235         struct pane *p, *p2;
236         int i;
237
238         p = pane_root(ci->focus);
239         p2 = call_ret(pane, "attach-input", p);
240         if (p2)
241                 pane_reparent(disp, p2);
242         p = disp;
243         for (i = 0; initial_panes[i]; i++) {
244                 const char *cmd = initial_panes[i];
245                 if (cmd)
246                         p2 = call_ret(pane, cmd, p);
247                 if (p2)
248                         p = p2;
249         }
250         comm_call(ci->comm2, "cb", p);
251         return 1;
252 }
253
254 DEF_CMD(editor_multicall)
255 {
256         struct ed_info *ei = ci->home->data;
257         struct map *map = ei->map;
258         int ret;
259         const char *key = ci->key;
260
261         ((struct cmd_info*)ci)->key = ksuffix(ci, "global-multicall-");
262         ret = key_lookup_prefix(map, ci);
263         ((struct cmd_info*)ci)->key = key;
264         return ret;
265 }
266
267 DEF_CMD(editor_request_notify)
268 {
269         pane_add_notify(ci->focus, ci->home, ksuffix(ci, "editor:request:"));
270         return 1;
271 }
272
273 DEF_CMD(editor_send_notify)
274 {
275         /* editor:notify:... */
276         return home_pane_notify(ci->home, ksuffix(ci, "editor:notify:"),
277                                 ci->focus,
278                                 ci->num, ci->mark, ci->str,
279                                 ci->num2, ci->mark2, ci->str2, ci->comm2);
280 }
281
282 DEF_CMD(editor_free_panes)
283 {
284         struct ed_info *ei = ci->home->data;
285
286         while (ei->freelist) {
287                 struct pane *p = ei->freelist;
288                 ei->freelist = p->focus;
289                 p->focus = NULL;
290
291                 p->damaged &= ~DAMAGED_DEAD;
292                 pane_call(p, "Free", p);
293                 command_put(p->handle);
294                 p->handle = NULL;
295                 attr_free(&p->attrs);
296                 pane_put(p);
297         }
298         return 1;
299 }
300
301 DEF_CMD(editor_free_marks)
302 {
303         struct ed_info *ei = ci->home->data;
304
305         while (ei->mark_free_list) {
306                 struct mark *m = ei->mark_free_list;
307                 ei->mark_free_list = (struct mark*)m->all.next;
308                 __mark_free(m);
309         }
310
311         return 1;
312 }
313
314 DEF_CMD(editor_free_store)
315 {
316         struct ed_info *ei = ci->home->data;
317
318         while (ei->store) {
319                 struct store *s = ei->store;
320                 ei->store = s->next;
321                 free(s);
322         }
323         return 1;
324 }
325
326 DEF_EXTERN_CMD(edlib_do_free)
327 {
328         if (ci->home->data_size)
329                 unalloc_buf_safe(ci->home->data, ci->home->data_size, pane);
330         return 1;
331 }
332
333 /* FIXME I should be able to remove things from a keymap, not
334  * replace with this.
335  */
336 DEF_EXTERN_CMD(edlib_noop)
337 {
338         return Efallthrough;
339 }
340
341 DEF_CMD(editor_close)
342 {
343         stat_free();
344         return Efallthrough;
345 }
346
347 DEF_CMD(editor_free)
348 {
349         /* Freeing the ed_info here mustn't happen.  It must be
350          * done much later
351          */
352         return 1;
353 }
354
355 void * safe memsave(struct pane *p safe, const char *buf, int len)
356 {
357         struct ed_info *ei;
358
359         p = pane_root(p);
360         ei = p->data;
361         ASSERT(ei->magic==ED_MAGIC);
362         if (!ei->store)
363                 call_comm("event:on-idle", p, &editor_free_store, 2);
364         if (ei->store == NULL || ei->store->size < len) {
365                 struct store *s;
366                 int l = 4096 - sizeof(*s);
367                 while (l < len)
368                         l += 4096;
369                 s = malloc(l + sizeof(*s));
370                 s->next = ei->store;
371                 s->size = l;
372                 ei->store = s;
373         }
374         ei->store->size -= len;
375         if (buf)
376                 return memcpy(ei->store->space+ei->store->size, buf, len);
377         else
378                 return ei->store->space+ei->store->size;
379 }
380
381 char *strsave(struct pane *p safe, const char *buf)
382 {
383         if (!buf)
384                 return NULL;
385         return memsave(p, buf, strlen(buf)+1);
386 }
387
388 char *strnsave(struct pane *p safe, const char *buf, int len)
389 {
390         char *s;
391         if (!buf)
392                 return NULL;
393         s = memsave(p, buf, len+1);
394         if (s)
395                 s[len] = 0;
396         return s;
397 }
398
399 char * safe __strconcat(struct pane *p, const char *s1 safe, ...)
400 {
401         va_list ap;
402         char *s;
403         int len = 0;
404         char *ret;
405
406         len = strlen(s1);
407         va_start(ap, s1);
408         while ((s = va_arg(ap, char*)) != NULL)
409                 len += strlen(s);
410         va_end(ap);
411
412         if (p)
413                 ret = memsave(p, NULL, len+1);
414         else
415                 ret = malloc(len+1);
416         strcpy(ret, s1);
417         va_start(ap, s1);
418         while ((s = va_arg(ap, char*)) != NULL)
419                 strcat(ret, s);
420         va_end(ap);
421         return ret;
422 }
423
424 void editor_delayed_free(struct pane *ed safe, struct pane *p safe)
425 {
426         struct ed_info *ei = ed->data;
427         if (!ei) {
428                 p->damaged &= ~DAMAGED_DEAD;
429                 pane_call(p, "Free", p);
430                 command_put(p->handle);
431                 p->handle = NULL;
432                 attr_free(&p->attrs);
433                 pane_free(p);
434                 return;
435         }
436         ASSERT(ei->magic==ED_MAGIC);
437         if (!ei->freelist)
438                 call_comm("event:on-idle", ed, &editor_free_panes, 2);
439         p->focus = ei->freelist;
440         ei->freelist = p;
441 }
442
443 void editor_delayed_mark_free(struct mark *m safe)
444 {
445         struct pane *ed = pane_root(m->owner);
446         struct ed_info *ei = ed ? ed->data : NULL;
447
448         if (!ei) {
449                 __mark_free(m);
450                 return;
451         }
452         ASSERT(ei->magic==ED_MAGIC);
453         if (!ei->mark_free_list)
454                 call_comm("event:on-idle", ed, &editor_free_marks, 2);
455         m->all.next = (void*)ei->mark_free_list;
456         ei->mark_free_list = m;
457 }
458
459 struct pane *editor_new(void)
460 {
461         struct pane *ed;
462         struct ed_info *ei;
463
464         alloc(ei, pane);
465         ei->magic = ED_MAGIC;
466         ei->testing = (getenv("EDLIB_TESTING") != NULL);
467         if (! (void*) ed_map) {
468                 ed_map = key_alloc();
469                 key_add(ed_map, "global-set-attr", &global_set_attr);
470                 key_add(ed_map, "global-set-command", &global_set_command);
471                 key_add(ed_map, "global-set-command-prefix", &global_set_command);
472                 key_add(ed_map, "global-get-command", &global_get_command);
473                 key_add(ed_map, "global-load-module", &editor_load_module);
474                 key_add(ed_map, "global-config-dir", &global_config_dir);
475                 key_add_prefix(ed_map, "event:", &editor_auto_event);
476                 key_add_prefix(ed_map, "global-multicall-", &editor_multicall);
477                 key_add_prefix(ed_map, "editor:request:",
478                                &editor_request_notify);
479                 key_add_prefix(ed_map, "editor:notify:",
480                                &editor_send_notify);
481                 key_add(ed_map, "editor:activate-display",
482                         &editor_activate_display);
483                 key_add(ed_map, "Close", &editor_close);
484                 key_add(ed_map, "Free", &editor_free);
485         }
486         ei->map = key_alloc();
487         key_add_chain(ei->map, ed_map);
488         ei->cmd = ed_handle;
489         ei->cmd.m = &ei->map;
490         ed = pane_register_root(&ei->cmd.c, ei, sizeof(ei));
491
492         if (ed) {
493                 doc_setup(ed);
494                 log_setup(ed);
495         }
496         return ed;
497 }