cmd-if-shell.c revision 1.9 1 /* $OpenBSD$ */
2
3 /*
4 * Copyright (c) 2009 Tiago Cunha <me (at) tiagocunha.org>
5 * Copyright (c) 2009 Nicholas Marriott <nicm (at) 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 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 char *file;
53 u_int line;
54
55 char *cmd_if;
56 char *cmd_else;
57
58 struct client *client;
59 struct cmdq_item *item;
60 struct mouse_event mouse;
61 };
62
63 static enum cmd_retval
64 cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
65 {
66 struct args *args = self->args;
67 struct cmdq_shared *shared = item->shared;
68 struct cmd_if_shell_data *cdata;
69 char *shellcmd, *cmd, *cause;
70 struct cmd_list *cmdlist;
71 struct cmdq_item *new_item;
72 struct client *c = cmd_find_client(item, NULL, 1);
73 struct session *s = item->target.s;
74 struct winlink *wl = item->target.wl;
75 struct window_pane *wp = item->target.wp;
76
77 shellcmd = format_single(item, args->argv[0], c, s, wl, wp);
78 if (args_has(args, 'F')) {
79 cmd = NULL;
80 if (*shellcmd != '0' && *shellcmd != '\0')
81 cmd = args->argv[1];
82 else if (args->argc == 3)
83 cmd = args->argv[2];
84 free(shellcmd);
85 if (cmd == NULL)
86 return (CMD_RETURN_NORMAL);
87 cmdlist = cmd_string_parse(cmd, NULL, 0, &cause);
88 if (cmdlist == NULL) {
89 if (cause != NULL) {
90 cmdq_error(item, "%s", cause);
91 free(cause);
92 }
93 return (CMD_RETURN_ERROR);
94 }
95 new_item = cmdq_get_command(cmdlist, NULL, &shared->mouse, 0);
96 cmdq_insert_after(item, new_item);
97 cmd_list_free(cmdlist);
98 return (CMD_RETURN_NORMAL);
99 }
100
101 cdata = xcalloc(1, sizeof *cdata);
102 if (self->file != NULL) {
103 cdata->file = xstrdup(self->file);
104 cdata->line = self->line;
105 }
106
107 cdata->cmd_if = xstrdup(args->argv[1]);
108 if (args->argc == 3)
109 cdata->cmd_else = xstrdup(args->argv[2]);
110 else
111 cdata->cmd_else = NULL;
112
113 cdata->client = item->client;
114 if (cdata->client != NULL)
115 cdata->client->references++;
116
117 if (!args_has(args, 'b'))
118 cdata->item = item;
119 else
120 cdata->item = NULL;
121 memcpy(&cdata->mouse, &shared->mouse, sizeof cdata->mouse);
122
123 if (job_run(shellcmd, s, server_client_get_cwd(item->client, s), NULL,
124 cmd_if_shell_callback, cmd_if_shell_free, cdata, 0) == NULL) {
125 cmdq_error(item, "failed to run command: %s", shellcmd);
126 free(shellcmd);
127 free(cdata);
128 return (CMD_RETURN_ERROR);
129 }
130 free(shellcmd);
131
132 if (args_has(args, 'b'))
133 return (CMD_RETURN_NORMAL);
134 return (CMD_RETURN_WAIT);
135 }
136
137 static void
138 cmd_if_shell_callback(struct job *job)
139 {
140 struct cmd_if_shell_data *cdata = job_get_data(job);
141 struct client *c = cdata->client;
142 struct cmd_list *cmdlist;
143 struct cmdq_item *new_item;
144 char *cause, *cmd, *file = cdata->file;
145 u_int line = cdata->line;
146 int status;
147
148 status = job_get_status(job);
149 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
150 cmd = cdata->cmd_else;
151 else
152 cmd = cdata->cmd_if;
153 if (cmd == NULL)
154 goto out;
155
156 cmdlist = cmd_string_parse(cmd, file, line, &cause);
157 if (cmdlist == NULL) {
158 if (cause != NULL && cdata->item != NULL)
159 cmdq_error(cdata->item, "%s", cause);
160 free(cause);
161 new_item = NULL;
162 } else {
163 new_item = cmdq_get_command(cmdlist, NULL, &cdata->mouse, 0);
164 cmd_list_free(cmdlist);
165 }
166
167 if (new_item != NULL) {
168 if (cdata->item == NULL)
169 cmdq_append(c, new_item);
170 else
171 cmdq_insert_after(cdata->item, new_item);
172 }
173
174 out:
175 if (cdata->item != NULL)
176 cdata->item->flags &= ~CMDQ_WAITING;
177 }
178
179 static void
180 cmd_if_shell_free(void *data)
181 {
182 struct cmd_if_shell_data *cdata = data;
183
184 if (cdata->client != NULL)
185 server_client_unref(cdata->client);
186
187 free(cdata->cmd_else);
188 free(cdata->cmd_if);
189
190 free(cdata->file);
191 free(cdata);
192 }
193