1 /* $OpenBSD: cmd-if-shell.c,v 1.65 2019/07/17 17:49:23 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 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