Home | History | Annotate | Line # | Download | only in dist
cmd-if-shell.c revision 1.10
      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 	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(__UNCONST(cdata->input.file));
    219 
    220 	free(cdata);
    221 }
    222