1 /* $OpenBSD: cmd-command-prompt.c,v 1.55 2021/06/10 07:52:56 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 21 #include <ctype.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <time.h> 25 26 #include "tmux.h" 27 28 /* 29 * Prompt for command in client. 30 */ 31 32 static enum cmd_retval cmd_command_prompt_exec(struct cmd *, 33 struct cmdq_item *); 34 35 static int cmd_command_prompt_callback(struct client *, void *, 36 const char *, int); 37 static void cmd_command_prompt_free(void *); 38 39 const struct cmd_entry cmd_command_prompt_entry = { 40 .name = "command-prompt", 41 .alias = NULL, 42 43 .args = { "1FkiI:Np:t:T:", 0, 1 }, 44 .usage = "[-1FkiN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE 45 " [-T type] [template]", 46 47 .flags = CMD_CLIENT_TFLAG, 48 .exec = cmd_command_prompt_exec 49 }; 50 51 struct cmd_command_prompt_cdata { 52 int flags; 53 enum prompt_type prompt_type; 54 55 char *inputs; 56 char *next_input; 57 58 char *prompts; 59 char *next_prompt; 60 61 char *template; 62 int idx; 63 }; 64 65 static enum cmd_retval 66 cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item) 67 { 68 struct args *args = cmd_get_args(self); 69 struct client *tc = cmdq_get_target_client(item); 70 struct cmd_find_state *target = cmdq_get_target(item); 71 const char *inputs, *prompts, *type; 72 struct cmd_command_prompt_cdata *cdata; 73 char *prompt, *ptr, *input = NULL; 74 size_t n; 75 76 if (tc->prompt_string != NULL) 77 return (CMD_RETURN_NORMAL); 78 79 cdata = xcalloc(1, sizeof *cdata); 80 81 cdata->inputs = NULL; 82 cdata->next_input = NULL; 83 84 cdata->prompts = NULL; 85 cdata->next_prompt = NULL; 86 87 cdata->template = NULL; 88 cdata->idx = 1; 89 90 if (args->argc != 0 && args_has(args, 'F')) 91 cdata->template = format_single_from_target(item, args->argv[0]); 92 else if (args->argc != 0) 93 cdata->template = xstrdup(args->argv[0]); 94 else 95 cdata->template = xstrdup("%1"); 96 97 if ((prompts = args_get(args, 'p')) != NULL) 98 cdata->prompts = xstrdup(prompts); 99 else if (args->argc != 0) { 100 n = strcspn(cdata->template, " ,"); 101 xasprintf(&cdata->prompts, "(%.*s) ", (int) n, cdata->template); 102 } else 103 cdata->prompts = xstrdup(":"); 104 105 /* Get first prompt. */ 106 cdata->next_prompt = cdata->prompts; 107 ptr = strsep(&cdata->next_prompt, ","); 108 if (prompts == NULL) 109 prompt = xstrdup(ptr); 110 else 111 xasprintf(&prompt, "%s ", ptr); 112 113 /* Get initial prompt input. */ 114 if ((inputs = args_get(args, 'I')) != NULL) { 115 cdata->inputs = xstrdup(inputs); 116 cdata->next_input = cdata->inputs; 117 input = strsep(&cdata->next_input, ","); 118 } 119 120 /* Get prompt type. */ 121 if ((type = args_get(args, 'T')) != NULL) { 122 cdata->prompt_type = status_prompt_type(type); 123 if (cdata->prompt_type == PROMPT_TYPE_INVALID) { 124 cmdq_error(item, "unknown type: %s", type); 125 return (CMD_RETURN_ERROR); 126 } 127 } else 128 cdata->prompt_type = PROMPT_TYPE_COMMAND; 129 130 if (args_has(args, '1')) 131 cdata->flags |= PROMPT_SINGLE; 132 else if (args_has(args, 'N')) 133 cdata->flags |= PROMPT_NUMERIC; 134 else if (args_has(args, 'i')) 135 cdata->flags |= PROMPT_INCREMENTAL; 136 else if (args_has(args, 'k')) 137 cdata->flags |= PROMPT_KEY; 138 status_prompt_set(tc, target, prompt, input, 139 cmd_command_prompt_callback, cmd_command_prompt_free, cdata, 140 cdata->flags, cdata->prompt_type); 141 free(prompt); 142 143 return (CMD_RETURN_NORMAL); 144 } 145 146 static int 147 cmd_command_prompt_callback(struct client *c, void *data, const char *s, 148 int done) 149 { 150 struct cmd_command_prompt_cdata *cdata = data; 151 char *new_template, *prompt, *ptr, *error; 152 char *input = NULL; 153 enum cmd_parse_status status; 154 155 if (s == NULL) 156 return (0); 157 if (done && (cdata->flags & PROMPT_INCREMENTAL)) 158 return (0); 159 160 new_template = cmd_template_replace(cdata->template, s, cdata->idx); 161 if (done) { 162 free(cdata->template); 163 cdata->template = new_template; 164 } 165 166 /* 167 * Check if there are more prompts; if so, get its respective input 168 * and update the prompt data. 169 */ 170 if (done && (ptr = strsep(&cdata->next_prompt, ",")) != NULL) { 171 xasprintf(&prompt, "%s ", ptr); 172 input = strsep(&cdata->next_input, ","); 173 status_prompt_update(c, prompt, input); 174 175 free(prompt); 176 cdata->idx++; 177 return (1); 178 } 179 180 status = cmd_parse_and_append(new_template, NULL, c, NULL, &error); 181 if (status == CMD_PARSE_ERROR) { 182 cmdq_append(c, cmdq_get_error(error)); 183 free(error); 184 } 185 186 if (!done) 187 free(new_template); 188 if (c->prompt_inputcb != cmd_command_prompt_callback) 189 return (1); 190 return (0); 191 } 192 193 static void 194 cmd_command_prompt_free(void *data) 195 { 196 struct cmd_command_prompt_cdata *cdata = data; 197 198 free(cdata->inputs); 199 free(cdata->prompts); 200 free(cdata->template); 201 free(cdata); 202 } 203