2 * Copyright Neil Brown ©2023 <neil@brown.name>
3 * May be distributed under terms of GPLv2 - see file:COPYING
5 * Read an "ini" config file and set some attributes.
7 * global - set attr on editor
8 * module - set trigger to load module
9 * file:glob - set attributes when matching file visited
11 * When not in a section, or in the "include" section, include= will
20 #include "parse-ini.h"
22 static void load_config(const char *path safe, void *data);
24 static bool _glob_match(const char *patn safe, const char *path safe)
31 if (!*path || *path == '/')
38 if (_glob_match(patn+2, path))
41 if (_glob_match(patn+1, path))
60 static bool glob_match(const char *patn safe, const char *path safe)
63 if (patn[0] != '/' && !strstarts(patn, "**")) {
64 /* must match basename */
65 const char *sl = strrchr(path, '/');
69 ret = _glob_match(patn, path);
75 struct command appeared;
76 struct pane *root safe;
83 struct attrset *attrs;
85 } *triggers, *last_trigger;
88 static void add_trigger(struct config_data *cd safe, unsigned int type,
90 char *name safe, char *val safe, int append)
92 struct trigger *t = cd->last_trigger;
94 if (!t || strcmp(t->path, path) != 0 || t->type != type) {
96 t->path = strdup(path);
100 cd->last_trigger->next = t;
103 cd->last_trigger = t;
106 const char *old = attr_find(t->attrs, name);
108 val = strconcat(NULL, old, val);
109 attr_set_str(&t->attrs, name, val);
112 attr_set_str(&t->attrs, name, val);
114 attr_set_str(&t->attrs, name, val);
117 static void config_file(char *path safe, unsigned int type,
118 struct pane *doc safe,
119 struct config_data *cd safe)
123 for (t = cd->triggers; t; t = t->next)
124 if (t->type == type && glob_match(t->path, path)) {
127 while ((k = attr_get_next_key(t->attrs, k, -1, &val)) != NULL) {
128 if (strstarts(k, "APPEND "))
129 call("doc:append:", doc, 0, NULL, val,
132 call("doc:set:", doc, 0, NULL, val,
146 struct mod_cmd *mc = container_of(ci->comm, struct mod_cmd, c);
152 /* NOTE: this might free mc, so don't touch it again */
153 call("global-load-module", ci->home, 0, NULL, mc->module);
154 return home_call(ci->home, ci->key, ci->focus,
155 ci->num, ci->mark, ci->str,
156 ci->num2, ci->mark2, ci->str2,
157 ci->x, ci->y, ci->comm2);
160 static void al_free(struct command *c safe)
162 struct mod_cmd *mc = container_of(c, struct mod_cmd, c);
168 static void handle(void *data, char *section safe, char *name safe, char *value safe,
169 const char *path safe, int append)
171 struct config_data *cd;
177 if (strstarts(name, "TESTING ")) {
178 if (!edlib_testing(cd->root))
182 if (strstarts(name, "NOTESTING ")) {
183 if (edlib_testing(cd->root))
187 if (strcmp(section, "") == 0 || strcmp(section,"include") == 0) {
188 if (strcmp(name, "include") == 0) {
189 load_config(value, data);
195 if (strcmp(section, "global") == 0) {
196 call("global-set-attr", cd->root, append, NULL, name,
201 if (strcmp(section, "module") == 0 && value[0]) {
204 mc = malloc(sizeof(*mc));
205 mc->module = strdup(name);
208 mc->c.free = al_free;
209 if (strstarts(value, "PREFIX "))
210 call_comm("global-set-command-prefix", cd->root, &mc->c, 0, NULL,
213 call_comm("global-set-command", cd->root, &mc->c, 0, NULL,
218 if (strstarts(section, "file:")) {
219 add_trigger(cd, TRIGGER_FILE, section+5, name, value, append);
222 if (strstarts(section, "doc:")) {
223 add_trigger(cd, TRIGGER_DOC, section+4, name, value, append);
228 static void load_config(const char *path safe, void *data)
231 struct config_data *cd = data;
234 parse_ini(path, handle, data);
238 * Relative paths can be loaded using xdg-find-edlib-file data
240 p = call_ret(str, "xdg-find-edlib-file", cd->root, 0, NULL,
241 path, 0, NULL, "config");
242 if (p && access(p, F_OK) == 0)
243 parse_ini(p, handle, data);
247 static void config_free(struct command *c safe)
249 struct config_data *cd = container_of(c, struct config_data, c);
252 while ((t = cd->triggers) != NULL) {
253 cd->triggers = t->next;
255 attr_free(&t->attrs);
261 DEF_CMD(config_appeared)
263 struct config_data *cd = container_of(ci->comm, struct config_data, appeared);
264 char *path = pane_attr_get(ci->focus, "filename");
266 config_file(path, TRIGGER_FILE, ci->focus, cd);
269 path = pane_attr_get(ci->focus, "doc-name");
271 config_file(path, TRIGGER_DOC, ci->focus, cd);
279 struct config_data *cd;
280 if (ci->comm == &config_load) {
281 /* This is the first call - need to allocate storage
282 * and register a new command.
286 cd->c.free = config_free;
287 cd->appeared = config_appeared;
289 call_comm("global-set-command", ci->home, &cd->c, 0, NULL, "config-load");
290 call_comm("global-set-command", ci->home, &cd->appeared,
291 0, NULL, "doc:appeared-config");
293 cd = container_of(ci->comm, struct config_data, c);
296 load_config(ci->str, cd);
300 void edlib_init(struct pane *ed safe)
302 call_comm("global-set-command", ed, &config_load,
303 0, NULL, "config-load");