1 /* $OpenBSD: cmd-if-shell.c,v 1.49 2017/01/15 22:00:56 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 enum cmd_retval cmd_if_shell_error(struct cmdq_item *, void *); 35 static void cmd_if_shell_callback(struct job *); 36 static void cmd_if_shell_free(void *); 37 38 const struct cmd_entry cmd_if_shell_entry = { 39 .name = "if-shell", 40 .alias = "if", 41 42 .args = { "bFt:", 2, 3 }, 43 .usage = "[-bF] " CMD_TARGET_PANE_USAGE " shell-command command " 44 "[command]", 45 46 .tflag = CMD_PANE_CANFAIL, 47 48 .flags = 0, 49 .exec = cmd_if_shell_exec 50 }; 51 52 struct cmd_if_shell_data { 53 char *file; 54 u_int line; 55 56 char *cmd_if; 57 char *cmd_else; 58 59 struct client *client; 60 struct cmdq_item *item; 61 struct mouse_event mouse; 62 }; 63 64 static enum cmd_retval 65 cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) 66 { 67 struct args *args = self->args; 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 session *s = item->state.tflag.s; 73 struct winlink *wl = item->state.tflag.wl; 74 struct window_pane *wp = item->state.tflag.wp; 75 struct format_tree *ft; 76 const char *cwd; 77 78 if (item->client != NULL && item->client->session == NULL) 79 cwd = item->client->cwd; 80 else if (s != NULL) 81 cwd = s->cwd; 82 else 83 cwd = NULL; 84 85 ft = format_create(item, 0); 86 format_defaults(ft, item->state.c, s, wl, wp); 87 shellcmd = format_expand(ft, args->argv[0]); 88 format_free(ft); 89 90 if (args_has(args, 'F')) { 91 cmd = NULL; 92 if (*shellcmd != '0' && *shellcmd != '\0') 93 cmd = args->argv[1]; 94 else if (args->argc == 3) 95 cmd = args->argv[2]; 96 free(shellcmd); 97 if (cmd == NULL) 98 return (CMD_RETURN_NORMAL); 99 cmdlist = cmd_string_parse(cmd, NULL, 0, &cause); 100 if (cmdlist == NULL) { 101 if (cause != NULL) { 102 cmdq_error(item, "%s", cause); 103 free(cause); 104 } 105 return (CMD_RETURN_ERROR); 106 } 107 new_item = cmdq_get_command(cmdlist, NULL, &item->mouse, 0); 108 cmdq_insert_after(item, new_item); 109 cmd_list_free(cmdlist); 110 return (CMD_RETURN_NORMAL); 111 } 112 113 cdata = xcalloc(1, sizeof *cdata); 114 if (self->file != NULL) { 115 cdata->file = xstrdup(self->file); 116 cdata->line = self->line; 117 } 118 119 cdata->cmd_if = xstrdup(args->argv[1]); 120 if (args->argc == 3) 121 cdata->cmd_else = xstrdup(args->argv[2]); 122 else 123 cdata->cmd_else = NULL; 124 125 cdata->client = item->client; 126 cdata->client->references++; 127 128 if (!args_has(args, 'b')) 129 cdata->item = item; 130 else 131 cdata->item = NULL; 132 memcpy(&cdata->mouse, &item->mouse, sizeof cdata->mouse); 133 134 job_run(shellcmd, s, cwd, cmd_if_shell_callback, cmd_if_shell_free, 135 cdata); 136 free(shellcmd); 137 138 if (args_has(args, 'b')) 139 return (CMD_RETURN_NORMAL); 140 return (CMD_RETURN_WAIT); 141 } 142 143 static enum cmd_retval 144 cmd_if_shell_error(struct cmdq_item *item, void *data) 145 { 146 char *error = data; 147 148 cmdq_error(item, "%s", error); 149 free(error); 150 151 return (CMD_RETURN_NORMAL); 152 } 153 154 static void 155 cmd_if_shell_callback(struct job *job) 156 { 157 struct cmd_if_shell_data *cdata = job->data; 158 struct client *c = cdata->client; 159 struct cmd_list *cmdlist; 160 struct cmdq_item *new_item; 161 char *cause, *cmd, *file = cdata->file; 162 u_int line = cdata->line; 163 164 if (!WIFEXITED(job->status) || WEXITSTATUS(job->status) != 0) 165 cmd = cdata->cmd_else; 166 else 167 cmd = cdata->cmd_if; 168 if (cmd == NULL) 169 goto out; 170 171 cmdlist = cmd_string_parse(cmd, file, line, &cause); 172 if (cmdlist == NULL) { 173 if (cause != NULL) 174 new_item = cmdq_get_callback(cmd_if_shell_error, cause); 175 else 176 new_item = NULL; 177 } else { 178 new_item = cmdq_get_command(cmdlist, NULL, &cdata->mouse, 0); 179 cmd_list_free(cmdlist); 180 } 181 182 if (new_item != NULL) { 183 if (cdata->item == NULL) 184 cmdq_append(c, new_item); 185 else 186 cmdq_insert_after(cdata->item, new_item); 187 } 188 189 out: 190 if (cdata->item != NULL) 191 cdata->item->flags &= ~CMDQ_WAITING; 192 } 193 194 static void 195 cmd_if_shell_free(void *data) 196 { 197 struct cmd_if_shell_data *cdata = data; 198 199 server_client_unref(cdata->client); 200 201 free(cdata->cmd_else); 202 free(cdata->cmd_if); 203 204 free(cdata->file); 205 free(cdata); 206 } 207