1 | /* $OpenBSD$ */ |
2 | |
3 | /* |
4 | * Copyright (c) 2009 Tiago Cunha <[email protected]> |
5 | * Copyright (c) 2009 Nicholas Marriott <[email protected]> |
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 | static enum cmd_retval cmd_if_shell_exec(struct cmd *, struct cmdq_item *); |
33 | |
34 | static void cmd_if_shell_callback(struct job *); |
35 | static void cmd_if_shell_free(void *); |
36 | |
37 | const struct cmd_entry cmd_if_shell_entry = { |
38 | .name = "if-shell" , |
39 | .alias = "if" , |
40 | |
41 | .args = { "bFt:" , 2, 3 }, |
42 | .usage = "[-bF] " CMD_TARGET_PANE_USAGE " shell-command command " |
43 | "[command]" , |
44 | |
45 | .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, |
46 | |
47 | .flags = 0, |
48 | .exec = cmd_if_shell_exec |
49 | }; |
50 | |
51 | struct cmd_if_shell_data { |
52 | struct cmd_parse_input input; |
53 | |
54 | char *cmd_if; |
55 | char *cmd_else; |
56 | |
57 | struct client *client; |
58 | struct cmdq_item *item; |
59 | struct mouse_event mouse; |
60 | }; |
61 | |
62 | static enum cmd_retval |
63 | cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) |
64 | { |
65 | struct args *args = self->args; |
66 | struct mouse_event *m = &item->shared->mouse; |
67 | struct cmd_if_shell_data *cdata; |
68 | char *shellcmd, *cmd; |
69 | struct cmdq_item *new_item; |
70 | struct cmd_find_state *fs = &item->target; |
71 | struct client *c = cmd_find_client(item, NULL, 1); |
72 | struct session *s = fs->s; |
73 | struct winlink *wl = fs->wl; |
74 | struct window_pane *wp = fs->wp; |
75 | struct cmd_parse_input pi; |
76 | struct cmd_parse_result *pr; |
77 | |
78 | shellcmd = format_single(item, args->argv[0], c, s, wl, wp); |
79 | if (args_has(args, 'F')) { |
80 | if (*shellcmd != '0' && *shellcmd != '\0') |
81 | cmd = args->argv[1]; |
82 | else if (args->argc == 3) |
83 | cmd = args->argv[2]; |
84 | else |
85 | cmd = NULL; |
86 | free(shellcmd); |
87 | if (cmd == NULL) |
88 | return (CMD_RETURN_NORMAL); |
89 | |
90 | memset(&pi, 0, sizeof pi); |
91 | if (self->file != NULL) |
92 | pi.file = self->file; |
93 | pi.line = self->line; |
94 | pi.item = item; |
95 | pi.c = c; |
96 | cmd_find_copy_state(&pi.fs, fs); |
97 | |
98 | pr = cmd_parse_from_string(cmd, &pi); |
99 | switch (pr->status) { |
100 | case CMD_PARSE_EMPTY: |
101 | break; |
102 | case CMD_PARSE_ERROR: |
103 | cmdq_error(item, "%s" , pr->error); |
104 | free(pr->error); |
105 | return (CMD_RETURN_ERROR); |
106 | case CMD_PARSE_SUCCESS: |
107 | new_item = cmdq_get_command(pr->cmdlist, fs, m, 0); |
108 | cmdq_insert_after(item, new_item); |
109 | cmd_list_free(pr->cmdlist); |
110 | break; |
111 | } |
112 | return (CMD_RETURN_NORMAL); |
113 | } |
114 | |
115 | cdata = xcalloc(1, sizeof *cdata); |
116 | |
117 | cdata->cmd_if = xstrdup(args->argv[1]); |
118 | if (args->argc == 3) |
119 | cdata->cmd_else = xstrdup(args->argv[2]); |
120 | else |
121 | cdata->cmd_else = NULL; |
122 | memcpy(&cdata->mouse, m, sizeof cdata->mouse); |
123 | |
124 | if (!args_has(args, 'b')) |
125 | cdata->client = item->client; |
126 | else |
127 | cdata->client = c; |
128 | if (cdata->client != NULL) |
129 | cdata->client->references++; |
130 | |
131 | if (!args_has(args, 'b')) |
132 | cdata->item = item; |
133 | else |
134 | cdata->item = NULL; |
135 | |
136 | memset(&cdata->input, 0, sizeof cdata->input); |
137 | if (self->file != NULL) |
138 | cdata->input.file = xstrdup(self->file); |
139 | cdata->input.line = self->line; |
140 | cdata->input.item = cdata->item; |
141 | cdata->input.c = c; |
142 | if (cdata->input.c != NULL) |
143 | cdata->input.c->references++; |
144 | cmd_find_copy_state(&cdata->input.fs, fs); |
145 | |
146 | if (job_run(shellcmd, s, server_client_get_cwd(item->client, s), NULL, |
147 | cmd_if_shell_callback, cmd_if_shell_free, cdata, 0) == NULL) { |
148 | cmdq_error(item, "failed to run command: %s" , shellcmd); |
149 | free(shellcmd); |
150 | free(cdata); |
151 | return (CMD_RETURN_ERROR); |
152 | } |
153 | free(shellcmd); |
154 | |
155 | if (args_has(args, 'b')) |
156 | return (CMD_RETURN_NORMAL); |
157 | return (CMD_RETURN_WAIT); |
158 | } |
159 | |
160 | static void |
161 | cmd_if_shell_callback(struct job *job) |
162 | { |
163 | struct cmd_if_shell_data *cdata = job_get_data(job); |
164 | struct client *c = cdata->client; |
165 | struct mouse_event *m = &cdata->mouse; |
166 | struct cmdq_item *new_item = NULL; |
167 | char *cmd; |
168 | int status; |
169 | struct cmd_parse_result *pr; |
170 | |
171 | status = job_get_status(job); |
172 | if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) |
173 | cmd = cdata->cmd_else; |
174 | else |
175 | cmd = cdata->cmd_if; |
176 | if (cmd == NULL) |
177 | goto out; |
178 | |
179 | pr = cmd_parse_from_string(cmd, &cdata->input); |
180 | switch (pr->status) { |
181 | case CMD_PARSE_EMPTY: |
182 | break; |
183 | case CMD_PARSE_ERROR: |
184 | if (cdata->item != NULL) |
185 | cmdq_error(cdata->item, "%s" , pr->error); |
186 | free(pr->error); |
187 | break; |
188 | case CMD_PARSE_SUCCESS: |
189 | new_item = cmdq_get_command(pr->cmdlist, NULL, m, 0); |
190 | cmd_list_free(pr->cmdlist); |
191 | break; |
192 | } |
193 | if (new_item != NULL) { |
194 | if (cdata->item == NULL) |
195 | cmdq_append(c, new_item); |
196 | else |
197 | cmdq_insert_after(cdata->item, new_item); |
198 | } |
199 | |
200 | out: |
201 | if (cdata->item != NULL) |
202 | cmdq_continue(cdata->item); |
203 | } |
204 | |
205 | static void |
206 | cmd_if_shell_free(void *data) |
207 | { |
208 | struct cmd_if_shell_data *cdata = data; |
209 | |
210 | if (cdata->client != NULL) |
211 | server_client_unref(cdata->client); |
212 | |
213 | free(cdata->cmd_else); |
214 | free(cdata->cmd_if); |
215 | |
216 | if (cdata->input.c != NULL) |
217 | server_client_unref(cdata->input.c); |
218 | free((void *)cdata->input.file); |
219 | |
220 | free(cdata); |
221 | } |
222 | |