1 /* autocmds.c -- run cmds on a per directory basis */
2
3 /*
4 * This file is part of CliFM
5 *
6 * Copyright (C) 2016-2021, L. Abramovich <johndoe.arch@outlook.com>
7 * All rights reserved.
8
9 * CliFM is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * CliFM is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22 * MA 02110-1301, USA.
23 */
24
25 #include "helpers.h"
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <glob.h>
31 #include <readline/readline.h>
32
33 #include "aux.h"
34 #include "colors.h"
35 #include "exec.h"
36 #include "listing.h"
37 #include "strings.h"
38
39 /* The opts struct contains option values previous to any autocommand
40 * call */
41 void
reset_opts(void)42 reset_opts(void)
43 {
44 opts.color_scheme = cur_cscheme;
45 opts.files_counter = files_counter;
46 opts.light_mode = light_mode;
47 opts.max_files = max_files;
48 opts.long_view = long_view;
49 opts.show_hidden = show_hidden;
50 opts.max_name_len = max_name_len;
51 opts.pager = pager;
52 opts.sort = sort;
53 opts.sort_reverse = sort_reverse;
54 }
55
56 /* Run autocommands for the current directory */
57 int
check_autocmds(void)58 check_autocmds(void)
59 {
60 int i = (int)autocmds_n, found = 0;
61 while (--i >= 0) {
62 if (!autocmds[i].pattern)
63 continue;
64
65 int rev = 0;
66 char *p = autocmds[i].pattern;
67 if (*p == '!') {
68 ++p;
69 rev = 1;
70 }
71
72 /* Double asterisk: match everything starting with PATTERN
73 * (less double asterisk itself and ending slash)*/
74 size_t plen = strlen(p), n = 0;
75 if (!rev && plen > 3 && p[plen - 1] == '*' && p[plen - 2] == '*') {
76 n = 2;
77 if (p[plen - 3] == '/')
78 n++;
79 if (*p == '~') {
80 p[plen - n] = '\0';
81 char *path = tilde_expand(p);
82 if (!path)
83 continue;
84 p[plen - n] = (n == 2 ? '*' : '/');
85 size_t tlen = strlen(path);
86 int ret = strncmp(path, ws[cur_ws].path, tlen);
87 free(path);
88 if (ret == 0) {
89 found = 1;
90 goto RUN_AUTOCMD;
91 }
92 } else { /* We have an absolute path */
93 if (strncmp(autocmds[i].pattern, ws[cur_ws].path, plen - n) == 0) {
94 found = 1;
95 goto RUN_AUTOCMD;
96 }
97 }
98 }
99
100 /* Glob expression or plain text for PATTERN */
101 glob_t g;
102 int ret = glob(p, GLOB_NOSORT | GLOB_NOCHECK
103 | GLOB_TILDE | GLOB_BRACE, NULL, &g);
104
105 if (ret != EXIT_SUCCESS) {
106 globfree(&g);
107 continue;
108 }
109
110 size_t j = 0;
111 for (; j < g.gl_pathc; j++) {
112 if (*ws[cur_ws].path == *g.gl_pathv[j]
113 && strcmp(ws[cur_ws].path, g.gl_pathv[j]) == 0) {
114 found = 1;
115 break;
116 }
117 }
118 globfree(&g);
119
120 if (!rev) {
121 if (!found)
122 continue;
123 } else if (found) {
124 continue;
125 }
126
127 RUN_AUTOCMD:
128 if (!autocmd_set) {
129 /* Backup current options, only if there was no autocmd for
130 * this directory */
131 opts.light_mode = light_mode;
132 opts.files_counter = files_counter;
133 opts.long_view = long_view;
134 opts.max_files = max_files;
135 opts.show_hidden = show_hidden;
136 opts.sort = sort;
137 opts.sort_reverse = sort_reverse;
138 opts.max_name_len = max_name_len;
139 opts.pager = pager;
140 if (autocmds[i].color_scheme)
141 opts.color_scheme = cur_cscheme;
142 autocmd_set = 1;
143 }
144
145 /* Set options for current directory */
146 if (autocmds[i].light_mode != -1)
147 light_mode = autocmds[i].light_mode;
148 if (autocmds[i].files_counter != -1)
149 files_counter = autocmds[i].files_counter;
150 if (autocmds[i].long_view != -1)
151 long_view = autocmds[i].long_view;
152 if (autocmds[i].show_hidden != -1)
153 show_hidden = autocmds[i].show_hidden;
154 if (autocmds[i].pager != -1)
155 pager = autocmds[i].pager;
156 if (autocmds[i].sort != -1)
157 sort = autocmds[i].sort;
158 if (autocmds[i].sort_reverse != -1)
159 sort_reverse = autocmds[i].sort_reverse;
160 if (autocmds[i].max_name_len != -1)
161 max_name_len = autocmds[i].max_name_len;
162 if (autocmds[i].max_files != -2)
163 max_files = autocmds[i].max_files;
164 if (autocmds[i].color_scheme)
165 set_colors(autocmds[i].color_scheme, 0);
166 if (autocmds[i].cmd) {
167 // if (*autocmds[i].cmd != '!') {
168 launch_execle(autocmds[i].cmd);
169 /* } else {
170 // size_t old_args_n = args_n;
171 free_dirlist();
172 char **cmd = parse_input_str(autocmds[i].cmd + 1);
173 if (!cmd)
174 break;
175 exec_cmd(cmd);
176 // args_n = old_args_n;
177 for (j = 0; cmd[j]; j++)
178 free(cmd[j]);
179 free(cmd);
180 } */
181 }
182
183 break;
184 }
185
186 return found;
187 }
188
189 /* Revert back to options previous to autocommand */
190 void
revert_autocmd_opts(void)191 revert_autocmd_opts(void)
192 {
193 light_mode = opts.light_mode;
194 files_counter = opts.files_counter;
195 long_view = opts.long_view;
196 max_files = opts.max_files;
197 show_hidden = opts.show_hidden;
198 max_name_len = opts.max_name_len;
199 pager = opts.pager;
200 sort = opts.sort;
201 sort_reverse = opts.sort_reverse;
202 if (opts.color_scheme && opts.color_scheme != cur_cscheme)
203 set_colors(opts.color_scheme, 0);
204 autocmd_set = 0;
205 }
206
207 void
free_autocmds(void)208 free_autocmds(void)
209 {
210 int i = (int)autocmds_n;
211 while (--i >= 0) {
212 free(autocmds[i].pattern);
213 free(autocmds[i].cmd);
214 }
215 free(autocmds);
216 autocmds = (struct autocmds_t *)NULL;
217 autocmds_n = 0;
218 autocmd_set = 0;
219
220 opts.color_scheme = (char *)NULL;
221 }
222
223 /* Store each autocommand option in the corresponding field of the
224 * autocmds struct */
225 static void
set_autocmd_opt(char * opt)226 set_autocmd_opt(char *opt)
227 {
228 if (!opt || !*opt)
229 return;
230
231 if (*opt == '!' && *(++opt)) {
232 free(autocmds[autocmds_n].cmd);
233 autocmds[autocmds_n].cmd = savestring(opt, strlen(opt));
234 return;
235 }
236
237 char *p = strchr(opt, '=');
238 if (!p || !*(++p))
239 return;
240
241 *(p - 1) = '\0';
242 if (*opt == 'c' && opt[1] == 's') {
243 int i = (int)cschemes_n;
244 while (--i >= 0) {
245 if (*color_schemes[i] == *p && strcmp(color_schemes[i], p) == 0) {
246 autocmds[autocmds_n].color_scheme = color_schemes[i];
247 break;
248 }
249 }
250 } else if (*opt == 'f' && opt[1] == 'c')
251 autocmds[autocmds_n].files_counter = atoi(p);
252 else if (*opt == 'h' && opt[1] == 'f')
253 autocmds[autocmds_n].show_hidden = atoi(p);
254 else if (*opt == 'l' && opt[1] == 'm')
255 autocmds[autocmds_n].light_mode = atoi(p);
256 else if (*opt == 'l' && opt[1] == 'v')
257 autocmds[autocmds_n].long_view = atoi(p);
258 else if (*opt == 'm' && opt[1] == 'f')
259 autocmds[autocmds_n].max_files = atoi(p);
260 else if (*opt == 'm' && opt[1] == 'n')
261 autocmds[autocmds_n].max_name_len = atoi(p);
262 else if (*opt == 'p' && opt[1] == 'g')
263 autocmds[autocmds_n].pager = atoi(p);
264 else if (*opt == 's' && opt[1] == 't')
265 autocmds[autocmds_n].sort = atoi(p);
266 else if (*opt == 's' && opt[1] == 'r')
267 autocmds[autocmds_n].sort_reverse = atoi(p);
268 }
269
270 static void
init_autocmd_opts()271 init_autocmd_opts()
272 {
273 autocmds[autocmds_n].cmd = (char *)NULL;
274 autocmds[autocmds_n].color_scheme = opts.color_scheme;
275 autocmds[autocmds_n].files_counter = opts.files_counter;
276 autocmds[autocmds_n].light_mode = opts.light_mode;
277 autocmds[autocmds_n].long_view = opts.long_view;
278 autocmds[autocmds_n].max_files = max_files;
279 autocmds[autocmds_n].max_name_len = max_name_len;
280 autocmds[autocmds_n].pager = opts.pager;
281 autocmds[autocmds_n].show_hidden = opts.show_hidden;
282 autocmds[autocmds_n].sort = sort;
283 autocmds[autocmds_n].sort_reverse = sort_reverse;
284 }
285
286 /* Take an autocmd line (from the config file) and store parameters
287 * in a struct */
288 void
parse_autocmd_line(char * cmd)289 parse_autocmd_line(char *cmd)
290 {
291 if (!cmd || !*cmd)
292 return;
293
294 size_t clen = strlen(cmd);
295 if (cmd[clen - 1] == '\n')
296 cmd[clen - 1] = '\0';
297
298 char *p = strchr(cmd, ' ');
299 if (!p || !*(p + 1))
300 return;
301
302 *p = '\0';
303
304 autocmds = (struct autocmds_t *)xrealloc(autocmds,
305 (autocmds_n + 1) * sizeof(struct autocmds_t));
306 autocmds[autocmds_n].pattern = savestring(cmd, strlen(cmd));
307
308 init_autocmd_opts();
309
310 char *q = ++p;
311 while (1) {
312 char *val = (char *)NULL;
313 if (*p == ',') {
314 *(p++) = '\0';
315 val = q;
316 q = p;
317 } else if (!*p) {
318 val = q;
319 } else {
320 ++p;
321 continue;
322 }
323
324 set_autocmd_opt(val);
325
326 if (!*p)
327 break;
328 }
329
330 autocmds_n++;
331 }
332