1 /* $OpenBSD: cmd-command-prompt.c,v 1.8 2009/08/19 10:39:50 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net> 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 <string.h> 23 24 #include "tmux.h" 25 26 /* 27 * Prompt for command in client. 28 */ 29 30 void cmd_command_prompt_init(struct cmd *, int); 31 int cmd_command_prompt_parse(struct cmd *, int, char **, char **); 32 int cmd_command_prompt_exec(struct cmd *, struct cmd_ctx *); 33 void cmd_command_prompt_free(struct cmd *); 34 size_t cmd_command_prompt_print(struct cmd *, char *, size_t); 35 36 int cmd_command_prompt_callback(void *, const char *); 37 void cmd_command_prompt_cfree(void *); 38 char *cmd_command_prompt_replace(char *, const char *, int); 39 40 const struct cmd_entry cmd_command_prompt_entry = { 41 "command-prompt", NULL, 42 CMD_TARGET_CLIENT_USAGE " [-p prompts] [template]", 43 0, 0, 44 cmd_command_prompt_init, 45 cmd_command_prompt_parse, 46 cmd_command_prompt_exec, 47 cmd_command_prompt_free, 48 cmd_command_prompt_print 49 }; 50 51 struct cmd_command_prompt_data { 52 char *prompts; 53 char *target; 54 char *template; 55 }; 56 57 struct cmd_command_prompt_cdata { 58 struct client *c; 59 char *next_prompt; 60 char *prompts; 61 char *template; 62 int idx; 63 }; 64 65 void 66 cmd_command_prompt_init(struct cmd *self, int key) 67 { 68 struct cmd_command_prompt_data *data; 69 70 self->data = data = xmalloc(sizeof *data); 71 data->prompts = NULL; 72 data->target = NULL; 73 data->template = NULL; 74 75 switch (key) { 76 case ',': 77 data->template = xstrdup("rename-window '%%'"); 78 break; 79 case '.': 80 data->template = xstrdup("move-window -t '%%'"); 81 break; 82 case 'f': 83 data->template = xstrdup("find-window '%%'"); 84 break; 85 } 86 } 87 88 int 89 cmd_command_prompt_parse(struct cmd *self, int argc, char **argv, char **cause) 90 { 91 struct cmd_command_prompt_data *data; 92 int opt; 93 94 self->entry->init(self, 0); 95 data = self->data; 96 97 while ((opt = getopt(argc, argv, "p:t:")) != -1) { 98 switch (opt) { 99 case 'p': 100 if (data->prompts == NULL) 101 data->prompts = xstrdup(optarg); 102 break; 103 case 't': 104 if (data->target == NULL) 105 data->target = xstrdup(optarg); 106 break; 107 default: 108 goto usage; 109 } 110 } 111 argc -= optind; 112 argv += optind; 113 if (argc != 0 && argc != 1) 114 goto usage; 115 116 if (argc == 1) 117 data->template = xstrdup(argv[0]); 118 119 return (0); 120 121 usage: 122 xasprintf(cause, "usage: %s %s", self->entry->name, self->entry->usage); 123 124 self->entry->free(self); 125 return (-1); 126 } 127 128 int 129 cmd_command_prompt_exec(struct cmd *self, struct cmd_ctx *ctx) 130 { 131 struct cmd_command_prompt_data *data = self->data; 132 struct cmd_command_prompt_cdata *cdata; 133 struct client *c; 134 char *prompt, *ptr; 135 size_t n; 136 137 if ((c = cmd_find_client(ctx, data->target)) == NULL) 138 return (-1); 139 140 if (c->prompt_string != NULL) 141 return (0); 142 143 cdata = xmalloc(sizeof *cdata); 144 cdata->c = c; 145 cdata->idx = 1; 146 cdata->next_prompt = NULL; 147 cdata->prompts = NULL; 148 cdata->template = NULL; 149 150 if (data->template != NULL) 151 cdata->template = xstrdup(data->template); 152 else 153 cdata->template = xstrdup("%1"); 154 if (data->prompts != NULL) 155 cdata->prompts = xstrdup(data->prompts); 156 else if (data->template != NULL) { 157 n = strcspn(data->template, " ,"); 158 xasprintf(&cdata->prompts, "(%.*s) ", (int) n, data->template); 159 } else 160 cdata->prompts = xstrdup(":"); 161 162 cdata->next_prompt = cdata->prompts; 163 ptr = strsep(&cdata->next_prompt, ","); 164 if (data->prompts == NULL) 165 prompt = xstrdup(ptr); 166 else 167 xasprintf(&prompt, "%s ", ptr); 168 status_prompt_set(c, prompt, cmd_command_prompt_callback, 169 cmd_command_prompt_cfree, cdata, 0); 170 xfree(prompt); 171 172 return (0); 173 } 174 175 void 176 cmd_command_prompt_free(struct cmd *self) 177 { 178 struct cmd_command_prompt_data *data = self->data; 179 180 if (data->prompts != NULL) 181 xfree(data->prompts); 182 if (data->target != NULL) 183 xfree(data->target); 184 if (data->template != NULL) 185 xfree(data->template); 186 xfree(data); 187 } 188 189 size_t 190 cmd_command_prompt_print(struct cmd *self, char *buf, size_t len) 191 { 192 struct cmd_command_prompt_data *data = self->data; 193 size_t off = 0; 194 195 off += xsnprintf(buf, len, "%s", self->entry->name); 196 if (data == NULL) 197 return (off); 198 if (off < len && data->prompts != NULL) 199 off += cmd_prarg(buf + off, len - off, " -p ", data->prompts); 200 if (off < len && data->target != NULL) 201 off += cmd_prarg(buf + off, len - off, " -t ", data->target); 202 if (off < len && data->template != NULL) 203 off += cmd_prarg(buf + off, len - off, " ", data->template); 204 return (off); 205 } 206 207 int 208 cmd_command_prompt_callback(void *data, const char *s) 209 { 210 struct cmd_command_prompt_cdata *cdata = data; 211 struct client *c = cdata->c; 212 struct cmd_list *cmdlist; 213 struct cmd_ctx ctx; 214 char *cause, *newtempl, *prompt, *ptr; 215 216 if (s == NULL) 217 return (0); 218 219 newtempl = cmd_command_prompt_replace(cdata->template, s, cdata->idx); 220 xfree(cdata->template); 221 cdata->template = newtempl; 222 223 if ((ptr = strsep(&cdata->next_prompt, ",")) != NULL) { 224 xasprintf(&prompt, "%s ", ptr); 225 status_prompt_update(c, prompt); 226 xfree(prompt); 227 cdata->idx++; 228 return (1); 229 } 230 231 if (cmd_string_parse(newtempl, &cmdlist, &cause) != 0) { 232 if (cause != NULL) { 233 *cause = toupper((u_char) *cause); 234 status_message_set(c, "%s", cause); 235 xfree(cause); 236 } 237 return (0); 238 } 239 240 ctx.msgdata = NULL; 241 ctx.cursession = c->session; 242 ctx.curclient = c; 243 244 ctx.error = key_bindings_error; 245 ctx.print = key_bindings_print; 246 ctx.info = key_bindings_info; 247 248 ctx.cmdclient = NULL; 249 250 cmd_list_exec(cmdlist, &ctx); 251 cmd_list_free(cmdlist); 252 253 if (c->prompt_callbackfn != (void *) &cmd_command_prompt_callback) 254 return (1); 255 return (0); 256 } 257 258 void 259 cmd_command_prompt_cfree(void *data) 260 { 261 struct cmd_command_prompt_cdata *cdata = data; 262 263 if (cdata->prompts != NULL) 264 xfree(cdata->prompts); 265 if (cdata->template != NULL) 266 xfree(cdata->template); 267 xfree(cdata); 268 } 269 270 char * 271 cmd_command_prompt_replace(char *template, const char *s, int idx) 272 { 273 char ch; 274 char *buf, *ptr; 275 int replaced; 276 size_t len; 277 278 if (strstr(template, "%") == NULL) 279 return (xstrdup(template)); 280 281 buf = xmalloc(1); 282 *buf = '\0'; 283 len = 0; 284 replaced = 0; 285 286 ptr = template; 287 while (*ptr != '\0') { 288 switch (ch = *ptr++) { 289 case '%': 290 if (*ptr < '1' || *ptr > '9' || *ptr - '0' != idx) { 291 if (*ptr != '%' || replaced) 292 break; 293 replaced = 1; 294 } 295 ptr++; 296 297 len += strlen(s); 298 buf = xrealloc(buf, 1, len + 1); 299 strlcat(buf, s, len + 1); 300 continue; 301 } 302 buf = xrealloc(buf, 1, len + 2); 303 buf[len++] = ch; 304 buf[len] = '\0'; 305 } 306 307 return (buf); 308 } 309