xref: /openbsd-src/usr.bin/tmux/cmd-if-shell.c (revision 03a1f8dda56324449ca261682738d697ada96801)
1*03a1f8ddSnicm /* $OpenBSD: cmd-if-shell.c,v 1.84 2021/10/11 10:55:30 nicm Exp $ */
24983507eSnicm 
34983507eSnicm /*
44983507eSnicm  * Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
5cd21dd5dSnicm  * Copyright (c) 2009 Nicholas Marriott <nicm@openbsd.org>
64983507eSnicm  *
74983507eSnicm  * Permission to use, copy, modify, and distribute this software for any
84983507eSnicm  * purpose with or without fee is hereby granted, provided that the above
94983507eSnicm  * copyright notice and this permission notice appear in all copies.
104983507eSnicm  *
114983507eSnicm  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
124983507eSnicm  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
134983507eSnicm  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
144983507eSnicm  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
154983507eSnicm  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
164983507eSnicm  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
174983507eSnicm  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
184983507eSnicm  */
194983507eSnicm 
204983507eSnicm #include <sys/types.h>
21cd21dd5dSnicm #include <sys/wait.h>
224983507eSnicm 
2367a3a2b6Snicm #include <ctype.h>
247d053cf9Snicm #include <stdlib.h>
254983507eSnicm #include <string.h>
264983507eSnicm 
274983507eSnicm #include "tmux.h"
284983507eSnicm 
294983507eSnicm /*
3012828c2bSnicm  * Executes a tmux command if a shell command returns true or false.
314983507eSnicm  */
324983507eSnicm 
33e4c0b811Snicm static enum args_parse_type	cmd_if_shell_args_parse(struct args *, u_int,
34e4c0b811Snicm 				    char **);
35e4c0b811Snicm static enum cmd_retval		cmd_if_shell_exec(struct cmd *,
36e4c0b811Snicm 				    struct cmdq_item *);
374983507eSnicm 
38dc1f0f5fSnicm static void	cmd_if_shell_callback(struct job *);
39dc1f0f5fSnicm static void	cmd_if_shell_free(void *);
404983507eSnicm 
414983507eSnicm const struct cmd_entry cmd_if_shell_entry = {
42c057646bSnicm 	.name = "if-shell",
43c057646bSnicm 	.alias = "if",
44c057646bSnicm 
45e4c0b811Snicm 	.args = { "bFt:", 2, 3, cmd_if_shell_args_parse },
46c057646bSnicm 	.usage = "[-bF] " CMD_TARGET_PANE_USAGE " shell-command command "
47c057646bSnicm 		 "[command]",
48c057646bSnicm 
49bf0d297eSnicm 	.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
508d471e80Snicm 
518d471e80Snicm 	.flags = 0,
52c057646bSnicm 	.exec = cmd_if_shell_exec
534983507eSnicm };
544983507eSnicm 
55cd21dd5dSnicm struct cmd_if_shell_data {
5667a3a2b6Snicm 	struct args_command_state	*cmd_if;
5767a3a2b6Snicm 	struct args_command_state	*cmd_else;
58ea090d8cSnicm 
59765b9a58Snicm 	struct client			*client;
6068e0a7f2Snicm 	struct cmdq_item		*item;
61cd21dd5dSnicm };
624983507eSnicm 
63e4c0b811Snicm static enum args_parse_type
cmd_if_shell_args_parse(__unused struct args * args,u_int idx,__unused char ** cause)64e4c0b811Snicm cmd_if_shell_args_parse(__unused struct args *args, u_int idx,
65e4c0b811Snicm     __unused char **cause)
66e4c0b811Snicm {
67e4c0b811Snicm 	if (idx == 1 || idx == 2)
68e4c0b811Snicm 		return (ARGS_PARSE_COMMANDS_OR_STRING);
69e4c0b811Snicm 	return (ARGS_PARSE_STRING);
70e4c0b811Snicm }
71e4c0b811Snicm 
72dc1f0f5fSnicm static enum cmd_retval
cmd_if_shell_exec(struct cmd * self,struct cmdq_item * item)7368e0a7f2Snicm cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
744983507eSnicm {
7590d7ba38Snicm 	struct args			*args = cmd_get_args(self);
76040343aeSnicm 	struct cmd_find_state		*target = cmdq_get_target(item);
77cd21dd5dSnicm 	struct cmd_if_shell_data	*cdata;
782db6a388Snicm 	struct cmdq_item		*new_item;
792db6a388Snicm 	char				*shellcmd;
80035dc73dSnicm 	struct client			*tc = cmdq_get_target_client(item);
81040343aeSnicm 	struct session			*s = target->s;
8267a3a2b6Snicm 	struct cmd_list			*cmdlist;
831693b10bSnicm 	u_int				 count = args_count(args);
8467a3a2b6Snicm 	int				 wait = !args_has(args, 'b');
851549639cSnicm 
861693b10bSnicm 	shellcmd = format_single_from_target(item, args_string(args, 0));
87e3ea15f3Snicm 	if (args_has(args, 'F')) {
88e3ea15f3Snicm 		if (*shellcmd != '0' && *shellcmd != '\0')
890b5b9347Snicm 			cmdlist = args_make_commands_now(self, item, 1, 0);
901693b10bSnicm 		else if (count == 3)
910b5b9347Snicm 			cmdlist = args_make_commands_now(self, item, 2, 0);
922db6a388Snicm 		else {
9359fc359aSnicm 			free(shellcmd);
94e3ea15f3Snicm 			return (CMD_RETURN_NORMAL);
95df6ab229Snicm 		}
962db6a388Snicm 		free(shellcmd);
972db6a388Snicm 		if (cmdlist == NULL)
982db6a388Snicm 			return (CMD_RETURN_ERROR);
992db6a388Snicm 		new_item = cmdq_get_command(cmdlist, cmdq_get_state(item));
1002db6a388Snicm 		cmdq_insert_after(item, new_item);
101e3ea15f3Snicm 		return (CMD_RETURN_NORMAL);
102e3ea15f3Snicm 	}
103e3ea15f3Snicm 
10410c3dabaSnicm 	cdata = xcalloc(1, sizeof *cdata);
105ea090d8cSnicm 
10667a3a2b6Snicm 	cdata->cmd_if = args_make_commands_prepare(self, item, 1, NULL, wait,
10767a3a2b6Snicm 	    0);
1082db6a388Snicm 	if (count == 3) {
10967a3a2b6Snicm 		cdata->cmd_else = args_make_commands_prepare(self, item, 2,
11067a3a2b6Snicm 		    NULL, wait, 0);
1112db6a388Snicm 	}
112ea090d8cSnicm 
11367a3a2b6Snicm 	if (wait) {
114040343aeSnicm 		cdata->client = cmdq_get_client(item);
11567a3a2b6Snicm 		cdata->item = item;
11667a3a2b6Snicm 	} else
117035dc73dSnicm 		cdata->client = tc;
118cb1db415Snicm 	if (cdata->client != NULL)
119765b9a58Snicm 		cdata->client->references++;
120cd21dd5dSnicm 
121*03a1f8ddSnicm 	if (job_run(shellcmd, 0, NULL, NULL, s,
122040343aeSnicm 	    server_client_get_cwd(cmdq_get_client(item), s), NULL,
123a6396ca2Snicm 	    cmd_if_shell_callback, cmd_if_shell_free, cdata, 0, -1,
124a6396ca2Snicm 	    -1) == NULL) {
12504131cdaSnicm 		cmdq_error(item, "failed to run command: %s", shellcmd);
12604131cdaSnicm 		free(shellcmd);
12704131cdaSnicm 		free(cdata);
12804131cdaSnicm 		return (CMD_RETURN_ERROR);
12904131cdaSnicm 	}
1300a0dfdd8Snicm 	free(shellcmd);
131cd21dd5dSnicm 
13267a3a2b6Snicm 	if (!wait)
133175d36ccSnicm 		return (CMD_RETURN_NORMAL);
134175d36ccSnicm 	return (CMD_RETURN_WAIT);
135cd21dd5dSnicm }
136cd21dd5dSnicm 
137dc1f0f5fSnicm static void
cmd_if_shell_callback(struct job * job)138cd21dd5dSnicm cmd_if_shell_callback(struct job *job)
139cd21dd5dSnicm {
1409430fe9eSnicm 	struct cmd_if_shell_data	*cdata = job_get_data(job);
141765b9a58Snicm 	struct client			*c = cdata->client;
1422db6a388Snicm 	struct cmdq_item		*item = cdata->item, *new_item;
14367a3a2b6Snicm 	struct args_command_state	*state;
1442db6a388Snicm 	struct cmd_list			*cmdlist;
14567a3a2b6Snicm 	char				*error;
1469430fe9eSnicm 	int				 status;
147175d36ccSnicm 
1489430fe9eSnicm 	status = job_get_status(job);
1499430fe9eSnicm 	if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
15067a3a2b6Snicm 		state = cdata->cmd_else;
151175d36ccSnicm 	else
15267a3a2b6Snicm 		state = cdata->cmd_if;
15367a3a2b6Snicm 	if (state == NULL)
154765b9a58Snicm 		goto out;
155175d36ccSnicm 
15667a3a2b6Snicm 	cmdlist = args_make_commands(state, 0, NULL, &error);
15767a3a2b6Snicm 	if (cmdlist == NULL) {
15867a3a2b6Snicm 		if (cdata->item == NULL) {
15967a3a2b6Snicm 			*error = toupper((u_char)*error);
16067a3a2b6Snicm 			status_message_set(c, -1, 1, 0, "%s", error);
16167a3a2b6Snicm 		} else
16267a3a2b6Snicm 			cmdq_error(cdata->item, "%s", error);
16367a3a2b6Snicm 		free(error);
16467a3a2b6Snicm 	} else if (item == NULL) {
1652db6a388Snicm 		new_item = cmdq_get_command(cmdlist, NULL);
16668e0a7f2Snicm 		cmdq_append(c, new_item);
1672db6a388Snicm 	} else {
1682db6a388Snicm 		new_item = cmdq_get_command(cmdlist, cmdq_get_state(item));
1692db6a388Snicm 		cmdq_insert_after(item, new_item);
170765b9a58Snicm 	}
171175d36ccSnicm 
172765b9a58Snicm out:
17368e0a7f2Snicm 	if (cdata->item != NULL)
174780ca620Snicm 		cmdq_continue(cdata->item);
175175d36ccSnicm }
176175d36ccSnicm 
177dc1f0f5fSnicm static void
cmd_if_shell_free(void * data)178cd21dd5dSnicm cmd_if_shell_free(void *data)
1794983507eSnicm {
180cd21dd5dSnicm 	struct cmd_if_shell_data	*cdata = data;
1814983507eSnicm 
182cb1db415Snicm 	if (cdata->client != NULL)
183765b9a58Snicm 		server_client_unref(cdata->client);
1844983507eSnicm 
1852db6a388Snicm 	if (cdata->cmd_else != NULL)
18667a3a2b6Snicm 		args_make_commands_free(cdata->cmd_else);
18767a3a2b6Snicm 	args_make_commands_free(cdata->cmd_if);
188df6ab229Snicm 
1897d053cf9Snicm 	free(cdata);
1904983507eSnicm }
191