xref: /openbsd/usr.bin/tmux/cmd-if-shell.c (revision 8529ddd3)
1 /* $OpenBSD: cmd-if-shell.c,v 1.32 2015/04/24 22:19:36 nicm Exp $ */
2 
3 /*
4  * Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
5  * Copyright (c) 2009 Nicholas Marriott <nicm@openbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
16  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
17  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/types.h>
21 #include <sys/wait.h>
22 
23 #include <stdlib.h>
24 #include <string.h>
25 
26 #include "tmux.h"
27 
28 /*
29  * Executes a tmux command if a shell command returns true or false.
30  */
31 
32 enum cmd_retval	 cmd_if_shell_exec(struct cmd *, struct cmd_q *);
33 
34 void	cmd_if_shell_callback(struct job *);
35 void	cmd_if_shell_done(struct cmd_q *);
36 void	cmd_if_shell_free(void *);
37 
38 const struct cmd_entry cmd_if_shell_entry = {
39 	"if-shell", "if",
40 	"bFt:", 2, 3,
41 	"[-bF] " CMD_TARGET_PANE_USAGE " shell-command command [command]",
42 	0,
43 	cmd_if_shell_exec
44 };
45 
46 struct cmd_if_shell_data {
47 	char			*cmd_if;
48 	char			*cmd_else;
49 
50 	struct cmd_q		*cmdq;
51 	struct mouse_event	 mouse;
52 
53 	int			 bflag;
54 	int			 references;
55 };
56 
57 enum cmd_retval
58 cmd_if_shell_exec(struct cmd *self, struct cmd_q *cmdq)
59 {
60 	struct args			*args = self->args;
61 	struct cmd_if_shell_data	*cdata;
62 	char				*shellcmd, *cmd, *cause;
63 	struct cmd_list			*cmdlist;
64 	struct client			*c;
65 	struct session			*s = NULL;
66 	struct winlink			*wl = NULL;
67 	struct window_pane		*wp = NULL;
68 	struct format_tree		*ft;
69 	int				 cwd;
70 
71 	if (args_has(args, 't')) {
72 		wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp);
73 		cwd = wp->cwd;
74 	} else {
75 		c = cmd_find_client(cmdq, NULL, 1);
76 		if (c != NULL && c->session != NULL) {
77 			s = c->session;
78 			wl = s->curw;
79 			wp = wl->window->active;
80 		}
81 		if (cmdq->client != NULL && cmdq->client->session == NULL)
82 			cwd = cmdq->client->cwd;
83 		else if (s != NULL)
84 			cwd = s->cwd;
85 		else
86 			cwd = -1;
87 	}
88 
89 	ft = format_create();
90 	format_defaults(ft, NULL, s, wl, wp);
91 	shellcmd = format_expand(ft, args->argv[0]);
92 	format_free(ft);
93 
94 	if (args_has(args, 'F')) {
95 		cmd = NULL;
96 		if (*shellcmd != '0' && *shellcmd != '\0')
97 			cmd = args->argv[1];
98 		else if (args->argc == 3)
99 			cmd = args->argv[2];
100 		if (cmd == NULL)
101 			return (CMD_RETURN_NORMAL);
102 		if (cmd_string_parse(cmd, &cmdlist, NULL, 0, &cause) != 0) {
103 			if (cause != NULL) {
104 				cmdq_error(cmdq, "%s", cause);
105 				free(cause);
106 			}
107 			return (CMD_RETURN_ERROR);
108 		}
109 		cmdq_run(cmdq, cmdlist, &cmdq->item->mouse);
110 		cmd_list_free(cmdlist);
111 		return (CMD_RETURN_NORMAL);
112 	}
113 
114 	cdata = xmalloc(sizeof *cdata);
115 
116 	cdata->cmd_if = xstrdup(args->argv[1]);
117 	if (args->argc == 3)
118 		cdata->cmd_else = xstrdup(args->argv[2]);
119 	else
120 		cdata->cmd_else = NULL;
121 
122 	cdata->bflag = args_has(args, 'b');
123 
124 	cdata->cmdq = cmdq;
125 	memcpy(&cdata->mouse, &cmdq->item->mouse, sizeof cdata->mouse);
126 	cmdq->references++;
127 
128 	cdata->references = 1;
129 	job_run(shellcmd, s, cwd, cmd_if_shell_callback, cmd_if_shell_free,
130 	    cdata);
131 	free(shellcmd);
132 
133 	if (cdata->bflag)
134 		return (CMD_RETURN_NORMAL);
135 	return (CMD_RETURN_WAIT);
136 }
137 
138 void
139 cmd_if_shell_callback(struct job *job)
140 {
141 	struct cmd_if_shell_data	*cdata = job->data;
142 	struct cmd_q			*cmdq = cdata->cmdq, *cmdq1;
143 	struct cmd_list			*cmdlist;
144 	char				*cause, *cmd;
145 
146 	if (cmdq->dead)
147 		return;
148 
149 	if (!WIFEXITED(job->status) || WEXITSTATUS(job->status) != 0)
150 		cmd = cdata->cmd_else;
151 	else
152 		cmd = cdata->cmd_if;
153 	if (cmd == NULL)
154 		return;
155 
156 	if (cmd_string_parse(cmd, &cmdlist, NULL, 0, &cause) != 0) {
157 		if (cause != NULL) {
158 			cmdq_error(cmdq, "%s", cause);
159 			free(cause);
160 		}
161 		return;
162 	}
163 
164 	cmdq1 = cmdq_new(cmdq->client);
165 	cmdq1->emptyfn = cmd_if_shell_done;
166 	cmdq1->data = cdata;
167 
168 	cdata->references++;
169 	cmdq_run(cmdq1, cmdlist, &cdata->mouse);
170 	cmd_list_free(cmdlist);
171 }
172 
173 void
174 cmd_if_shell_done(struct cmd_q *cmdq1)
175 {
176 	struct cmd_if_shell_data	*cdata = cmdq1->data;
177 	struct cmd_q			*cmdq = cdata->cmdq;
178 
179 	if (cmdq1->client_exit >= 0)
180 		cmdq->client_exit = cmdq1->client_exit;
181 	cmdq_free(cmdq1);
182 
183 	if (--cdata->references != 0)
184 		return;
185 
186 	if (!cmdq_free(cmdq) && !cdata->bflag)
187 		cmdq_continue(cmdq);
188 
189 	free(cdata->cmd_else);
190 	free(cdata->cmd_if);
191 	free(cdata);
192 }
193 
194 void
195 cmd_if_shell_free(void *data)
196 {
197 	struct cmd_if_shell_data	*cdata = data;
198 	struct cmd_q			*cmdq = cdata->cmdq;
199 
200 	if (--cdata->references != 0)
201 		return;
202 
203 	if (!cmdq_free(cmdq) && !cdata->bflag)
204 		cmdq_continue(cmdq);
205 
206 	free(cdata->cmd_else);
207 	free(cdata->cmd_if);
208 	free(cdata);
209 }
210