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