2 * Copyright Neil Brown ©2015-2023 <neil@brown.name>
3 * May be distributed under terms of GPLv2 - see file:COPYING
15 #define PANE_DATA_TYPE struct ed_info
20 #define ED_MAGIC 0x4321fedcUL
22 static struct map *ed_map safe;
25 struct pane *freelist;
26 struct mark *mark_free_list;
28 struct lookup_cmd cmd;
29 /* These two paths contain nul-terminated strings,
30 * with a double-nul at the end.
43 #include "core-pane.h"
45 bool edlib_testing(struct pane *p safe)
47 struct ed_info *ei = pane_root(p)->data;
51 DEF_LOOKUP_CMD(ed_handle, ed_map);
53 DEF_CMD(global_set_attr)
59 attr_set_str(&ci->home->attrs, ci->str, ci->str2);
65 v = attr_find(ci->home->attrs, ci->str);
67 attr_set_str(&ci->home->attrs, ci->str, ci->str2);
70 v = strconcat(ci->home, v, ci->str2);
71 attr_set_str(&ci->home->attrs, ci->str, v);
75 DEF_CMD(global_set_command)
77 struct ed_info *ei = ci->home->data;
78 struct map *map = ei->map;
79 bool prefix = strcmp(ci->key, "global-set-command-prefix") == 0;
84 char *e = strconcat(NULL, ci->str, "\xFF\xFF\xFF\xFF");
85 key_add_range(map, ci->str, e, ci->comm2);
88 key_add_range(map, ci->str, ci->str2, ci->comm2);
90 key_add(map, ci->str, ci->comm2);
94 DEF_CMD(global_get_command)
96 struct ed_info *ei = ci->home->data;
97 struct map *map = ei->map;
101 !(cm = key_lookup_cmd(map, ci->str)))
103 return comm_call(ci->comm2, "callback:comm", ci->focus,
105 0, NULL, NULL, 0,0, cm);
108 DEF_CMD(global_config_dir)
110 const char *var = ci->str;
111 char *dir; // ci->str2;
112 char *key, *val = NULL;
113 struct pane *p = ci->home;
116 /* var might be different in different directories.
117 * Config setting are attributes stored on root that
118 * look like "config:var:dir".
119 * We find the best and return that with the dir
121 if (!var || !ci->str2 || !ci->comm2)
123 key = strconcat(p, "config:", var, ":", ci->str2);
124 dir = key + 7 + strlen(var) + 1;
125 end = dir + strlen(dir);
126 while (!val && end > dir) {
128 val = attr_find(p->attrs, key);
129 if (end[-1] == '/') {
130 while (end > dir && end[-1] == '/')
133 while (end > dir && end[-1] != '/')
139 comm_call(ci->comm2, "cb", ci->focus, 0, NULL, val,
145 #include "O/mod-list-decl.h"
146 typedef void init_func(struct pane *ed);
147 static struct builtin {
151 #include "O/mod-list.h"
154 DEF_CMD(editor_load_module)
156 struct ed_info *ei = ci->home->data;
157 struct map *map = ei->map;
158 const char *name = ci->str;
162 void (*s)(struct pane *p);
166 sprintf(buf, "edlib-%s.so", name);
167 /* RTLD_GLOBAL is needed for python, else we get
168 * errors about _Py_ZeroStruct which a python script
172 h = dlopen(buf, RTLD_NOW | RTLD_GLOBAL);
174 path = dlsym(h, "edlib_module_path");
176 if (dlinfo(h, RTLD_DI_ORIGIN, pbuf) == 0)
179 s = dlsym(h, "edlib_init");
181 char *v = dlsym(h, "edlib_version");
182 LOG("Loading %s - version %s", name, v ?: "not provided");
189 char *err = dlerror();
190 if (strstr(err, "No such file or directory") == NULL)
191 LOG("dlopen %s failed %s", buf, err);
197 for (i = 0; buf[i]; i++)
200 for (i = 0; i < sizeof(builtins)/sizeof(builtins[0]); i++)
201 if (strcmp(builtins[i].name, buf) == 0) {
202 builtins[i].func(ci->home);
206 /* Multiple modules can support module lookup, such as
207 * global-load-modules:python or global-load-module:lua.
208 * So do a prefix lookup.
210 if (key_lookup_prefix(map, ci, False) > 0)
212 LOG("Failed to load module: %s", name);
216 DEF_CMD(editor_auto_event)
218 /* Event handlers register under a private name so we
219 * have to use key_lookup_prefix to find them.
220 * If nothing is found, autoload lib-libevent (hack?)
222 struct ed_info *ei = ci->home->data;
223 struct map *map = ei->map;
224 int ret = key_lookup_prefix(map, ci, False);
228 if (strcmp(ci->key, "event:refresh") == 0)
229 /* pointless to autoload for refresh */
231 call("attach-libevent", ci->home);
232 return key_lookup_prefix(map, ci, False);
235 DEF_CMD(editor_activate_display)
237 /* Given a display attached to the root, integrate it
238 * into a full initial stack of panes.
240 struct pane *disp = ci->focus;
242 char *ip = attr_find(ci->home->attrs, "editor-initial-panes");
247 ip = strsave(ci->home, ip);
248 p = pane_root(ci->focus);
250 p2 = call_ret(pane, "attach-window-core", p);
255 for (t = strtok_r(ip, " \t\n", &save);
257 t = strtok_r(NULL, " \t\n", &save)) {
260 if (strcmp(t, "DISPLAY") == 0) {
262 pane_reparent(disp, p);
268 m = strconcat(NULL, "attach-", t);
269 p2 = call_ret(pane, m, p);
274 comm_call(ci->comm2, "cb", p);
278 DEF_CMD(editor_multicall)
280 struct ed_info *ei = ci->home->data;
281 struct map *map = ei->map;
283 const char *key = ci->key;
285 ((struct cmd_info*)ci)->key = ksuffix(ci, "global-multicall-");
286 ret = key_lookup_prefix(map, ci, False);
287 ((struct cmd_info*)ci)->key = key;
291 DEF_CMD(editor_request_notify)
293 pane_add_notify(ci->focus, ci->home, ksuffix(ci, "editor:request:"));
297 DEF_CMD(editor_send_notify)
299 /* editor:notify:... */
300 return home_pane_notify(ci->home, ksuffix(ci, "editor:notify:"),
302 ci->num, ci->mark, ci->str,
303 ci->num2, ci->mark2, ci->str2, ci->comm2);
306 DEF_CMD(editor_free_panes)
308 struct ed_info *ei = ci->home->data;
310 while (ei->freelist) {
311 struct pane *p = ei->freelist;
312 ei->freelist = p->focus;
315 command_put(p->handle);
317 attr_free(&p->attrs);
323 DEF_CMD(editor_free_marks)
325 struct ed_info *ei = ci->home->data;
327 while (ei->mark_free_list) {
328 struct mark *m = ei->mark_free_list;
329 ei->mark_free_list = (struct mark*)m->all.next;
336 DEF_CMD(editor_free_store)
338 struct ed_info *ei = ci->home->data;
341 struct store *s = ei->store;
348 /* FIXME I should be able to remove things from a keymap, not
351 DEF_EXTERN_CMD(edlib_noop)
356 DEF_CMD_CLOSED(editor_close)
358 struct ed_info *ei = ci->home->data;
360 free(ei->here); ei->here = NULL;
361 free(ei->data_path); ei->data_path = NULL;
362 free(ei->config_path); ei->config_path = NULL;
366 void * safe memsave(struct pane *p safe, const char *buf, int len)
372 ASSERT(ei->magic==ED_MAGIC);
374 call_comm("event:on-idle", p, &editor_free_store, 2);
375 if (ei->store == NULL || ei->store->size < len) {
377 int l = 4096 - sizeof(*s);
380 s = malloc(l + sizeof(*s));
385 ei->store->size -= len;
387 return memcpy(ei->store->space+ei->store->size, buf, len);
389 return ei->store->space+ei->store->size;
392 char *strsave(struct pane *p safe, const char *buf)
396 return memsave(p, buf, strlen(buf)+1);
399 char *strnsave(struct pane *p safe, const char *buf, int len)
404 s = memsave(p, buf, len+1);
410 char * safe do_strconcat(struct pane *p, const char *s1 safe, ...)
419 while ((s = va_arg(ap, char*)) != NULL)
424 ret = memsave(p, NULL, len+1);
429 while ((s = va_arg(ap, char*)) != NULL)
435 void editor_delayed_free(struct pane *ed safe, struct pane *p safe)
437 struct ed_info *ei = ed->data;
439 command_put(p->handle);
441 attr_free(&p->attrs);
445 ASSERT(ei->magic==ED_MAGIC);
447 call_comm("event:on-idle", ed, &editor_free_panes, 2);
448 p->focus = ei->freelist;
452 void editor_delayed_mark_free(struct mark *m safe)
454 struct pane *ed = pane_root(m->owner);
455 struct ed_info *ei = ed->data;
457 ASSERT(ei->magic==ED_MAGIC);
458 if (!ei->mark_free_list)
459 call_comm("event:on-idle", ed, &editor_free_marks, 2);
460 m->all.next = (void*)ei->mark_free_list;
461 ei->mark_free_list = m;
464 static char *set_here(struct pane *p safe)
466 struct ed_info *ei = p->data;
471 else if (dladdr(&set_here, &info) == 0)
472 ei->here = strdup("");
475 ei->here = strdup(info.dli_fname ?: "");
476 sl = strrchr(ei->here, '/');
483 static char *set_data_path(struct pane *p safe)
485 struct ed_info *ei = p->data;
486 char *dh, *dd, *here;
490 return ei->data_path;
493 dh = getenv("XDG_DATA_HOME");
495 char *h = getenv("HOME");
497 dh = strconcat(p, h, "/.local/share");
499 if (dh && *dh == '/') {
501 buf_concat(&b, "/edlib/");
502 buf_append_byte(&b, 0);
506 if (here && *here == '/') {
507 buf_concat(&b, here);
508 buf_concat(&b, "/edlib/");
509 buf_append_byte(&b, 0);
512 dd = getenv("XDG_DATA_DIRS");
514 dd = "/usr/local/share:/usr/share";
516 char *c = strchrnul(dd, ':');
518 buf_concat_len(&b, dd, c-dd);
519 buf_concat(&b, "/edlib/");
520 buf_append_byte(&b, 0);
527 ei->data_path = buf_final(&b);
530 return ei->data_path;
533 static char *set_config_path(struct pane *p safe)
535 struct ed_info *ei = p->data;
536 char *ch, *cd, *here;
540 return ei->config_path;
543 ch = getenv("XDG_CONFIG_HOME");
545 char *h = getenv("HOME");
547 ch = strconcat(p, h, "/.config");
549 if (ch && *ch == '/') {
551 buf_concat(&b, "/edlib/");
552 buf_append_byte(&b, 0);
556 if (here && *here == '/') {
557 buf_concat(&b, here);
558 buf_concat(&b, "/edlib/");
559 buf_append_byte(&b, 0);
562 cd = getenv("XDG_CONFIG_DIRS");
566 char *c = strchrnul(cd, ':');
568 buf_concat_len(&b, cd, c-cd);
569 buf_concat(&b, "/edlib/");
570 buf_append_byte(&b, 0);
577 ei->config_path = buf_final(&b);
580 return ei->config_path;
583 static char *set_bin_path(struct pane *p safe)
585 struct ed_info *ei = p->data;
594 if (here && *here == '/') {
595 buf_concat(&b, here);
597 strncmp(b.b + b.len-4, "/lib", 4) == 0)
600 buf_concat(&b, "/../");
601 buf_concat(&b, "bin/");
602 buf_append_byte(&b, 0);
606 bd = "/usr/bin:/usr/local/bin";
608 char *c = strchrnul(bd, ':');
610 buf_concat_len(&b, bd, c-bd);
611 buf_append_byte(&b, 0);
618 ei->bin_path = buf_final(&b);
624 DEF_CMD(global_find_file)
627 * ->str is a file basename. If it contains {COMM}, that will
628 * be replaced with the "command-name" attr from root, or
629 * "edlib" if nothing can be found.
630 * ->str2 is one of "data", "config", "bin"
631 * We find a file with basename in a known location following
632 * the XDG Base Directory Specificaton.
633 * https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
634 * but also look in the directory containing this library $HERE
635 * For "data" we look in a directory "edlib" under:
636 * - $XDG_DATA_HOME, or $HOME/.local/share
638 * - $XDG_DATA_DIRS, or /usr/local/share:/usr/share
640 * For config we look in a "edlib" under:
641 * - $XDG_CONFIG_HOME, or $HOME/.config
643 * - $XDG_CONFIG_DIRS, or /etc/xdg
645 * For bin we look in $HERE/../bin and $PATH
648 const char *base[2] = {ci->str, NULL};
652 if (base[0] == NULL || ci->str2 == NULL)
654 if (strcmp(ci->str2, "data") == 0)
655 path = set_data_path(ci->home);
656 else if (strcmp(ci->str2, "config") == 0)
657 path = set_config_path(ci->home);
658 else if (strcmp(ci->str2, "bin") == 0)
659 path = set_bin_path(ci->home);
663 cn = strstr(base[0], "{COMM}");
665 char *p = strndup(base[0], cn - base[0]);
666 char *comm = attr_find(ci->home->attrs, "command-name");
669 base[0] = strconcat(ci->home, p, comm, cn+6);
670 if (strcmp(comm, "edlib") != 0)
671 base[1] = strconcat(ci->home, p, "edlib", cn+6);
673 for (i = 0; i < 2 && base[i] ; i++) {
675 for (pth = path; pth && *pth; pth += strlen(pth)+1) {
676 char *p = strconcat(NULL, pth, base[i]);
680 fd = open(p, O_RDONLY);
686 comm_call(ci->comm2, "cb", ci->focus, 0, NULL, p);
694 static struct pane *editor;
695 static void catch(int sig)
705 ci.str = ci.str2 = NULL;
706 ci.mark = ci.mark2 = NULL;
710 ci.key = "fast-alarm-";
711 key_lookup_prefix(editor->data->map, &ci, True);
714 ci.key = "fast-interrupt-";
715 key_lookup_prefix(editor->data->map, &ci, True);
720 struct pane *editor_new(const char *comm_name)
725 if (! (void*) ed_map) {
726 ed_map = key_alloc();
727 key_add(ed_map, "global-set-attr", &global_set_attr);
728 key_add(ed_map, "global-set-command", &global_set_command);
729 key_add(ed_map, "global-set-command-prefix", &global_set_command);
730 key_add(ed_map, "global-get-command", &global_get_command);
731 key_add(ed_map, "global-load-module", &editor_load_module);
732 key_add(ed_map, "global-config-dir", &global_config_dir);
733 key_add(ed_map, "xdg-find-edlib-file", &global_find_file);
734 key_add_prefix(ed_map, "event:", &editor_auto_event);
735 key_add_prefix(ed_map, "global-multicall-", &editor_multicall);
736 key_add_prefix(ed_map, "editor:request:",
737 &editor_request_notify);
738 key_add_prefix(ed_map, "editor:notify:",
739 &editor_send_notify);
740 key_add(ed_map, "editor:activate-display",
741 &editor_activate_display);
742 key_add(ed_map, "Close", &editor_close);
744 ed = pane_register_root(&ed_handle.c, NULL, sizeof(*ei));
748 ei->magic = ED_MAGIC;
749 attr_set_str(&ed->attrs, "command-name", comm_name ?: "edlib");
750 ei->testing = (getenv("EDLIB_TESTING") != NULL);
751 ei->map = key_alloc();
752 key_add_chain(ei->map, ed_map);
754 ei->cmd.m = &ei->map;
755 /* This allows the pane to see registered commands */
756 pane_update_handle(ed, &ei->cmd.c);
762 signal(SIGINT, catch);
763 signal(SIGALRM, catch);