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
32static enum cmd_retval cmd_if_shell_exec(struct cmd *, struct cmdq_item *);
33
34static void cmd_if_shell_callback(struct job *);
35static void cmd_if_shell_free(void *);
36
37const 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
51struct 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
62static enum cmd_retval
63cmd_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
160static void
161cmd_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
200out:
201 if (cdata->item != NULL)
202 cmdq_continue(cdata->item);
203}
204
205static void
206cmd_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