xref: /openbsd-src/usr.bin/tmux/cmd-command-prompt.c (revision 3a89195f39ec989b56bbfd5a9a2b6ac968b019c6)
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