1 | /* $OpenBSD$ */ |
2 | |
3 | /* |
4 | * Copyright (c) 2007 Nicholas Marriott <[email protected]> |
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 | #include <sys/time.h> |
21 | |
22 | #include <fnmatch.h> |
23 | #include <pwd.h> |
24 | #include <stdlib.h> |
25 | #include <string.h> |
26 | #include <unistd.h> |
27 | |
28 | #include "tmux.h" |
29 | |
30 | extern const struct cmd_entry cmd_attach_session_entry; |
31 | extern const struct cmd_entry cmd_bind_key_entry; |
32 | extern const struct cmd_entry cmd_break_pane_entry; |
33 | extern const struct cmd_entry cmd_capture_pane_entry; |
34 | extern const struct cmd_entry cmd_choose_buffer_entry; |
35 | extern const struct cmd_entry cmd_choose_client_entry; |
36 | extern const struct cmd_entry cmd_choose_tree_entry; |
37 | extern const struct cmd_entry cmd_clear_history_entry; |
38 | extern const struct cmd_entry cmd_clock_mode_entry; |
39 | extern const struct cmd_entry cmd_command_prompt_entry; |
40 | extern const struct cmd_entry cmd_confirm_before_entry; |
41 | extern const struct cmd_entry cmd_copy_mode_entry; |
42 | extern const struct cmd_entry cmd_delete_buffer_entry; |
43 | extern const struct cmd_entry cmd_detach_client_entry; |
44 | extern const struct cmd_entry ; |
45 | extern const struct cmd_entry cmd_display_message_entry; |
46 | extern const struct cmd_entry cmd_display_panes_entry; |
47 | extern const struct cmd_entry cmd_down_pane_entry; |
48 | extern const struct cmd_entry cmd_find_window_entry; |
49 | extern const struct cmd_entry cmd_has_session_entry; |
50 | extern const struct cmd_entry cmd_if_shell_entry; |
51 | extern const struct cmd_entry cmd_join_pane_entry; |
52 | extern const struct cmd_entry cmd_kill_pane_entry; |
53 | extern const struct cmd_entry cmd_kill_server_entry; |
54 | extern const struct cmd_entry cmd_kill_session_entry; |
55 | extern const struct cmd_entry cmd_kill_window_entry; |
56 | extern const struct cmd_entry cmd_last_pane_entry; |
57 | extern const struct cmd_entry cmd_last_window_entry; |
58 | extern const struct cmd_entry cmd_link_window_entry; |
59 | extern const struct cmd_entry cmd_list_buffers_entry; |
60 | extern const struct cmd_entry cmd_list_clients_entry; |
61 | extern const struct cmd_entry cmd_list_commands_entry; |
62 | extern const struct cmd_entry cmd_list_keys_entry; |
63 | extern const struct cmd_entry cmd_list_panes_entry; |
64 | extern const struct cmd_entry cmd_list_sessions_entry; |
65 | extern const struct cmd_entry cmd_list_windows_entry; |
66 | extern const struct cmd_entry cmd_load_buffer_entry; |
67 | extern const struct cmd_entry cmd_lock_client_entry; |
68 | extern const struct cmd_entry cmd_lock_server_entry; |
69 | extern const struct cmd_entry cmd_lock_session_entry; |
70 | extern const struct cmd_entry cmd_move_pane_entry; |
71 | extern const struct cmd_entry cmd_move_window_entry; |
72 | extern const struct cmd_entry cmd_new_session_entry; |
73 | extern const struct cmd_entry cmd_new_window_entry; |
74 | extern const struct cmd_entry cmd_next_layout_entry; |
75 | extern const struct cmd_entry cmd_next_window_entry; |
76 | extern const struct cmd_entry cmd_paste_buffer_entry; |
77 | extern const struct cmd_entry cmd_pipe_pane_entry; |
78 | extern const struct cmd_entry cmd_previous_layout_entry; |
79 | extern const struct cmd_entry cmd_previous_window_entry; |
80 | extern const struct cmd_entry cmd_refresh_client_entry; |
81 | extern const struct cmd_entry cmd_rename_session_entry; |
82 | extern const struct cmd_entry cmd_rename_window_entry; |
83 | extern const struct cmd_entry cmd_resize_pane_entry; |
84 | extern const struct cmd_entry cmd_resize_window_entry; |
85 | extern const struct cmd_entry cmd_respawn_pane_entry; |
86 | extern const struct cmd_entry cmd_respawn_window_entry; |
87 | extern const struct cmd_entry cmd_rotate_window_entry; |
88 | extern const struct cmd_entry cmd_run_shell_entry; |
89 | extern const struct cmd_entry cmd_save_buffer_entry; |
90 | extern const struct cmd_entry cmd_select_layout_entry; |
91 | extern const struct cmd_entry cmd_select_pane_entry; |
92 | extern const struct cmd_entry cmd_select_window_entry; |
93 | extern const struct cmd_entry cmd_send_keys_entry; |
94 | extern const struct cmd_entry cmd_send_prefix_entry; |
95 | extern const struct cmd_entry cmd_set_buffer_entry; |
96 | extern const struct cmd_entry cmd_set_environment_entry; |
97 | extern const struct cmd_entry cmd_set_hook_entry; |
98 | extern const struct cmd_entry cmd_set_option_entry; |
99 | extern const struct cmd_entry cmd_set_window_option_entry; |
100 | extern const struct cmd_entry cmd_show_buffer_entry; |
101 | extern const struct cmd_entry cmd_show_environment_entry; |
102 | extern const struct cmd_entry cmd_show_hooks_entry; |
103 | extern const struct cmd_entry cmd_show_messages_entry; |
104 | extern const struct cmd_entry cmd_show_options_entry; |
105 | extern const struct cmd_entry cmd_show_window_options_entry; |
106 | extern const struct cmd_entry cmd_source_file_entry; |
107 | extern const struct cmd_entry cmd_split_window_entry; |
108 | extern const struct cmd_entry cmd_start_server_entry; |
109 | extern const struct cmd_entry cmd_suspend_client_entry; |
110 | extern const struct cmd_entry cmd_swap_pane_entry; |
111 | extern const struct cmd_entry cmd_swap_window_entry; |
112 | extern const struct cmd_entry cmd_switch_client_entry; |
113 | extern const struct cmd_entry cmd_unbind_key_entry; |
114 | extern const struct cmd_entry cmd_unlink_window_entry; |
115 | extern const struct cmd_entry cmd_up_pane_entry; |
116 | extern const struct cmd_entry cmd_wait_for_entry; |
117 | |
118 | const struct cmd_entry *cmd_table[] = { |
119 | &cmd_attach_session_entry, |
120 | &cmd_bind_key_entry, |
121 | &cmd_break_pane_entry, |
122 | &cmd_capture_pane_entry, |
123 | &cmd_choose_buffer_entry, |
124 | &cmd_choose_client_entry, |
125 | &cmd_choose_tree_entry, |
126 | &cmd_clear_history_entry, |
127 | &cmd_clock_mode_entry, |
128 | &cmd_command_prompt_entry, |
129 | &cmd_confirm_before_entry, |
130 | &cmd_copy_mode_entry, |
131 | &cmd_delete_buffer_entry, |
132 | &cmd_detach_client_entry, |
133 | &cmd_display_menu_entry, |
134 | &cmd_display_message_entry, |
135 | &cmd_display_panes_entry, |
136 | &cmd_find_window_entry, |
137 | &cmd_has_session_entry, |
138 | &cmd_if_shell_entry, |
139 | &cmd_join_pane_entry, |
140 | &cmd_kill_pane_entry, |
141 | &cmd_kill_server_entry, |
142 | &cmd_kill_session_entry, |
143 | &cmd_kill_window_entry, |
144 | &cmd_last_pane_entry, |
145 | &cmd_last_window_entry, |
146 | &cmd_link_window_entry, |
147 | &cmd_list_buffers_entry, |
148 | &cmd_list_clients_entry, |
149 | &cmd_list_commands_entry, |
150 | &cmd_list_keys_entry, |
151 | &cmd_list_panes_entry, |
152 | &cmd_list_sessions_entry, |
153 | &cmd_list_windows_entry, |
154 | &cmd_load_buffer_entry, |
155 | &cmd_lock_client_entry, |
156 | &cmd_lock_server_entry, |
157 | &cmd_lock_session_entry, |
158 | &cmd_move_pane_entry, |
159 | &cmd_move_window_entry, |
160 | &cmd_new_session_entry, |
161 | &cmd_new_window_entry, |
162 | &cmd_next_layout_entry, |
163 | &cmd_next_window_entry, |
164 | &cmd_paste_buffer_entry, |
165 | &cmd_pipe_pane_entry, |
166 | &cmd_previous_layout_entry, |
167 | &cmd_previous_window_entry, |
168 | &cmd_refresh_client_entry, |
169 | &cmd_rename_session_entry, |
170 | &cmd_rename_window_entry, |
171 | &cmd_resize_pane_entry, |
172 | &cmd_resize_window_entry, |
173 | &cmd_respawn_pane_entry, |
174 | &cmd_respawn_window_entry, |
175 | &cmd_rotate_window_entry, |
176 | &cmd_run_shell_entry, |
177 | &cmd_save_buffer_entry, |
178 | &cmd_select_layout_entry, |
179 | &cmd_select_pane_entry, |
180 | &cmd_select_window_entry, |
181 | &cmd_send_keys_entry, |
182 | &cmd_send_prefix_entry, |
183 | &cmd_set_buffer_entry, |
184 | &cmd_set_environment_entry, |
185 | &cmd_set_hook_entry, |
186 | &cmd_set_option_entry, |
187 | &cmd_set_window_option_entry, |
188 | &cmd_show_buffer_entry, |
189 | &cmd_show_environment_entry, |
190 | &cmd_show_hooks_entry, |
191 | &cmd_show_messages_entry, |
192 | &cmd_show_options_entry, |
193 | &cmd_show_window_options_entry, |
194 | &cmd_source_file_entry, |
195 | &cmd_split_window_entry, |
196 | &cmd_start_server_entry, |
197 | &cmd_suspend_client_entry, |
198 | &cmd_swap_pane_entry, |
199 | &cmd_swap_window_entry, |
200 | &cmd_switch_client_entry, |
201 | &cmd_unbind_key_entry, |
202 | &cmd_unlink_window_entry, |
203 | &cmd_wait_for_entry, |
204 | NULL |
205 | }; |
206 | |
207 | static u_int cmd_list_next_group = 1; |
208 | |
209 | void printflike(3, 4) |
210 | cmd_log_argv(int argc, char **argv, const char *fmt, ...) |
211 | { |
212 | char *prefix; |
213 | va_list ap; |
214 | int i; |
215 | |
216 | va_start(ap, fmt); |
217 | xvasprintf(&prefix, fmt, ap); |
218 | va_end(ap); |
219 | |
220 | for (i = 0; i < argc; i++) |
221 | log_debug("%s: argv[%d]=%s" , prefix, i, argv[i]); |
222 | free(prefix); |
223 | } |
224 | |
225 | void |
226 | cmd_prepend_argv(int *argc, char ***argv, char *arg) |
227 | { |
228 | char **new_argv; |
229 | int i; |
230 | |
231 | new_argv = xreallocarray(NULL, (*argc) + 1, sizeof *new_argv); |
232 | new_argv[0] = xstrdup(arg); |
233 | for (i = 0; i < *argc; i++) |
234 | new_argv[1 + i] = (*argv)[i]; |
235 | |
236 | free(*argv); |
237 | *argv = new_argv; |
238 | (*argc)++; |
239 | } |
240 | |
241 | void |
242 | cmd_append_argv(int *argc, char ***argv, char *arg) |
243 | { |
244 | *argv = xreallocarray(*argv, (*argc) + 1, sizeof **argv); |
245 | (*argv)[(*argc)++] = xstrdup(arg); |
246 | } |
247 | |
248 | int |
249 | cmd_pack_argv(int argc, char **argv, char *buf, size_t len) |
250 | { |
251 | size_t arglen; |
252 | int i; |
253 | |
254 | if (argc == 0) |
255 | return (0); |
256 | cmd_log_argv(argc, argv, "%s" , __func__); |
257 | |
258 | *buf = '\0'; |
259 | for (i = 0; i < argc; i++) { |
260 | if (strlcpy(buf, argv[i], len) >= len) |
261 | return (-1); |
262 | arglen = strlen(argv[i]) + 1; |
263 | buf += arglen; |
264 | len -= arglen; |
265 | } |
266 | |
267 | return (0); |
268 | } |
269 | |
270 | int |
271 | cmd_unpack_argv(char *buf, size_t len, int argc, char ***argv) |
272 | { |
273 | int i; |
274 | size_t arglen; |
275 | |
276 | if (argc == 0) |
277 | return (0); |
278 | *argv = xcalloc(argc, sizeof **argv); |
279 | |
280 | buf[len - 1] = '\0'; |
281 | for (i = 0; i < argc; i++) { |
282 | if (len == 0) { |
283 | cmd_free_argv(argc, *argv); |
284 | return (-1); |
285 | } |
286 | |
287 | arglen = strlen(buf) + 1; |
288 | (*argv)[i] = xstrdup(buf); |
289 | |
290 | buf += arglen; |
291 | len -= arglen; |
292 | } |
293 | cmd_log_argv(argc, *argv, "%s" , __func__); |
294 | |
295 | return (0); |
296 | } |
297 | |
298 | char ** |
299 | cmd_copy_argv(int argc, char **argv) |
300 | { |
301 | char **new_argv; |
302 | int i; |
303 | |
304 | if (argc == 0) |
305 | return (NULL); |
306 | new_argv = xcalloc(argc + 1, sizeof *new_argv); |
307 | for (i = 0; i < argc; i++) { |
308 | if (argv[i] != NULL) |
309 | new_argv[i] = xstrdup(argv[i]); |
310 | } |
311 | return (new_argv); |
312 | } |
313 | |
314 | void |
315 | cmd_free_argv(int argc, char **argv) |
316 | { |
317 | int i; |
318 | |
319 | if (argc == 0) |
320 | return; |
321 | for (i = 0; i < argc; i++) |
322 | free(argv[i]); |
323 | free(argv); |
324 | } |
325 | |
326 | char * |
327 | cmd_stringify_argv(int argc, char **argv) |
328 | { |
329 | char *buf; |
330 | int i; |
331 | size_t len; |
332 | |
333 | if (argc == 0) |
334 | return (xstrdup("" )); |
335 | |
336 | len = 0; |
337 | buf = NULL; |
338 | |
339 | for (i = 0; i < argc; i++) { |
340 | len += strlen(argv[i]) + 1; |
341 | buf = xrealloc(buf, len); |
342 | |
343 | if (i == 0) |
344 | *buf = '\0'; |
345 | else |
346 | strlcat(buf, " " , len); |
347 | strlcat(buf, argv[i], len); |
348 | } |
349 | return (buf); |
350 | } |
351 | |
352 | char * |
353 | cmd_get_alias(const char *name) |
354 | { |
355 | struct options_entry *o; |
356 | struct options_array_item *a; |
357 | union options_value *ov; |
358 | size_t wanted, n; |
359 | const char *equals; |
360 | |
361 | o = options_get_only(global_options, "command-alias" ); |
362 | if (o == NULL) |
363 | return (NULL); |
364 | wanted = strlen(name); |
365 | |
366 | a = options_array_first(o); |
367 | while (a != NULL) { |
368 | ov = options_array_item_value(a); |
369 | |
370 | equals = strchr(ov->string, '='); |
371 | if (equals != NULL) { |
372 | n = equals - ov->string; |
373 | if (n == wanted && strncmp(name, ov->string, n) == 0) |
374 | return (xstrdup(equals + 1)); |
375 | } |
376 | |
377 | a = options_array_next(a); |
378 | } |
379 | return (NULL); |
380 | } |
381 | |
382 | static const struct cmd_entry * |
383 | cmd_find(const char *name, char **cause) |
384 | { |
385 | const struct cmd_entry **loop, *entry, *found = NULL; |
386 | int ambiguous; |
387 | char s[8192]; |
388 | |
389 | ambiguous = 0; |
390 | for (loop = cmd_table; *loop != NULL; loop++) { |
391 | entry = *loop; |
392 | if (entry->alias != NULL && strcmp(entry->alias, name) == 0) { |
393 | ambiguous = 0; |
394 | found = entry; |
395 | break; |
396 | } |
397 | |
398 | if (strncmp(entry->name, name, strlen(name)) != 0) |
399 | continue; |
400 | if (found != NULL) |
401 | ambiguous = 1; |
402 | found = entry; |
403 | |
404 | if (strcmp(entry->name, name) == 0) |
405 | break; |
406 | } |
407 | if (ambiguous) |
408 | goto ambiguous; |
409 | if (found == NULL) { |
410 | xasprintf(cause, "unknown command: %s" , name); |
411 | return (NULL); |
412 | } |
413 | return (found); |
414 | |
415 | ambiguous: |
416 | *s = '\0'; |
417 | for (loop = cmd_table; *loop != NULL; loop++) { |
418 | entry = *loop; |
419 | if (strncmp(entry->name, name, strlen(name)) != 0) |
420 | continue; |
421 | if (strlcat(s, entry->name, sizeof s) >= sizeof s) |
422 | break; |
423 | if (strlcat(s, ", " , sizeof s) >= sizeof s) |
424 | break; |
425 | } |
426 | s[strlen(s) - 2] = '\0'; |
427 | xasprintf(cause, "ambiguous command: %s, could be: %s" , name, s); |
428 | return (NULL); |
429 | } |
430 | |
431 | struct cmd * |
432 | cmd_parse(int argc, char **argv, const char *file, u_int line, char **cause) |
433 | { |
434 | const struct cmd_entry *entry; |
435 | const char *name; |
436 | struct cmd *cmd; |
437 | struct args *args; |
438 | |
439 | if (argc == 0) { |
440 | xasprintf(cause, "no command" ); |
441 | return (NULL); |
442 | } |
443 | name = argv[0]; |
444 | |
445 | entry = cmd_find(name, cause); |
446 | if (entry == NULL) |
447 | return (NULL); |
448 | cmd_log_argv(argc, argv, "%s: %s" , __func__, entry->name); |
449 | |
450 | args = args_parse(entry->args.template, argc, argv); |
451 | if (args == NULL) |
452 | goto usage; |
453 | if (entry->args.lower != -1 && args->argc < entry->args.lower) |
454 | goto usage; |
455 | if (entry->args.upper != -1 && args->argc > entry->args.upper) |
456 | goto usage; |
457 | |
458 | cmd = xcalloc(1, sizeof *cmd); |
459 | cmd->entry = entry; |
460 | cmd->args = args; |
461 | |
462 | if (file != NULL) |
463 | cmd->file = xstrdup(file); |
464 | cmd->line = line; |
465 | |
466 | cmd->alias = NULL; |
467 | cmd->argc = argc; |
468 | cmd->argv = cmd_copy_argv(argc, argv); |
469 | |
470 | return (cmd); |
471 | |
472 | usage: |
473 | if (args != NULL) |
474 | args_free(args); |
475 | xasprintf(cause, "usage: %s %s" , entry->name, entry->usage); |
476 | return (NULL); |
477 | } |
478 | |
479 | void |
480 | cmd_free(struct cmd *cmd) |
481 | { |
482 | free(cmd->alias); |
483 | cmd_free_argv(cmd->argc, cmd->argv); |
484 | |
485 | free(cmd->file); |
486 | |
487 | args_free(cmd->args); |
488 | free(cmd); |
489 | } |
490 | |
491 | char * |
492 | cmd_print(struct cmd *cmd) |
493 | { |
494 | char *out, *s; |
495 | |
496 | s = args_print(cmd->args); |
497 | if (*s != '\0') |
498 | xasprintf(&out, "%s %s" , cmd->entry->name, s); |
499 | else |
500 | out = xstrdup(cmd->entry->name); |
501 | free(s); |
502 | |
503 | return (out); |
504 | } |
505 | |
506 | struct cmd_list * |
507 | cmd_list_new(void) |
508 | { |
509 | struct cmd_list *cmdlist; |
510 | |
511 | cmdlist = xcalloc(1, sizeof *cmdlist); |
512 | cmdlist->references = 1; |
513 | cmdlist->group = cmd_list_next_group++; |
514 | TAILQ_INIT(&cmdlist->list); |
515 | return (cmdlist); |
516 | } |
517 | |
518 | void |
519 | cmd_list_append(struct cmd_list *cmdlist, struct cmd *cmd) |
520 | { |
521 | cmd->group = cmdlist->group; |
522 | TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry); |
523 | } |
524 | |
525 | void |
526 | cmd_list_move(struct cmd_list *cmdlist, struct cmd_list *from) |
527 | { |
528 | struct cmd *cmd, *cmd1; |
529 | |
530 | TAILQ_FOREACH_SAFE(cmd, &from->list, qentry, cmd1) { |
531 | TAILQ_REMOVE(&from->list, cmd, qentry); |
532 | TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry); |
533 | } |
534 | cmdlist->group = cmd_list_next_group++; |
535 | } |
536 | |
537 | void |
538 | cmd_list_free(struct cmd_list *cmdlist) |
539 | { |
540 | struct cmd *cmd, *cmd1; |
541 | |
542 | if (--cmdlist->references != 0) |
543 | return; |
544 | |
545 | TAILQ_FOREACH_SAFE(cmd, &cmdlist->list, qentry, cmd1) { |
546 | TAILQ_REMOVE(&cmdlist->list, cmd, qentry); |
547 | cmd_free(cmd); |
548 | } |
549 | |
550 | free(cmdlist); |
551 | } |
552 | |
553 | char * |
554 | cmd_list_print(struct cmd_list *cmdlist, int escaped) |
555 | { |
556 | struct cmd *cmd; |
557 | char *buf, *this; |
558 | size_t len; |
559 | |
560 | len = 1; |
561 | buf = xcalloc(1, len); |
562 | |
563 | TAILQ_FOREACH(cmd, &cmdlist->list, qentry) { |
564 | this = cmd_print(cmd); |
565 | |
566 | len += strlen(this) + 4; |
567 | buf = xrealloc(buf, len); |
568 | |
569 | strlcat(buf, this, len); |
570 | if (TAILQ_NEXT(cmd, qentry) != NULL) { |
571 | if (escaped) |
572 | strlcat(buf, " \\; " , len); |
573 | else |
574 | strlcat(buf, " ; " , len); |
575 | } |
576 | |
577 | free(this); |
578 | } |
579 | |
580 | return (buf); |
581 | } |
582 | |
583 | /* Adjust current mouse position for a pane. */ |
584 | int |
585 | cmd_mouse_at(struct window_pane *wp, struct mouse_event *m, u_int *xp, |
586 | u_int *yp, int last) |
587 | { |
588 | u_int x, y; |
589 | |
590 | if (last) { |
591 | x = m->lx + m->ox; |
592 | y = m->ly + m->oy; |
593 | } else { |
594 | x = m->x + m->ox; |
595 | y = m->y + m->oy; |
596 | } |
597 | log_debug("%s: x=%u, y=%u%s" , __func__, x, y, last ? " (last)" : "" ); |
598 | |
599 | if (m->statusat == 0 && y >= m->statuslines) |
600 | y -= m->statuslines; |
601 | |
602 | if (x < wp->xoff || x >= wp->xoff + wp->sx) |
603 | return (-1); |
604 | if (y < wp->yoff || y >= wp->yoff + wp->sy) |
605 | return (-1); |
606 | |
607 | if (xp != NULL) |
608 | *xp = x - wp->xoff; |
609 | if (yp != NULL) |
610 | *yp = y - wp->yoff; |
611 | return (0); |
612 | } |
613 | |
614 | /* Get current mouse window if any. */ |
615 | struct winlink * |
616 | cmd_mouse_window(struct mouse_event *m, struct session **sp) |
617 | { |
618 | struct session *s; |
619 | struct window *w; |
620 | struct winlink *wl; |
621 | |
622 | if (!m->valid) |
623 | return (NULL); |
624 | if (m->s == -1 || (s = session_find_by_id(m->s)) == NULL) |
625 | return (NULL); |
626 | if (m->w == -1) |
627 | wl = s->curw; |
628 | else { |
629 | if ((w = window_find_by_id(m->w)) == NULL) |
630 | return (NULL); |
631 | wl = winlink_find_by_window(&s->windows, w); |
632 | } |
633 | if (sp != NULL) |
634 | *sp = s; |
635 | return (wl); |
636 | } |
637 | |
638 | /* Get current mouse pane if any. */ |
639 | struct window_pane * |
640 | cmd_mouse_pane(struct mouse_event *m, struct session **sp, |
641 | struct winlink **wlp) |
642 | { |
643 | struct winlink *wl; |
644 | struct window_pane *wp; |
645 | |
646 | if ((wl = cmd_mouse_window(m, sp)) == NULL) |
647 | return (NULL); |
648 | if ((wp = window_pane_find_by_id(m->wp)) == NULL) |
649 | return (NULL); |
650 | if (!window_has_pane(wl->window, wp)) |
651 | return (NULL); |
652 | |
653 | if (wlp != NULL) |
654 | *wlp = wl; |
655 | return (wp); |
656 | } |
657 | |
658 | /* Replace the first %% or %idx in template by s. */ |
659 | char * |
660 | cmd_template_replace(const char *template, const char *s, int idx) |
661 | { |
662 | char ch, *buf; |
663 | const char *ptr, *cp, quote[] = "\"\\$;~" ; |
664 | int replaced, quoted; |
665 | size_t len; |
666 | |
667 | if (strchr(template, '%') == NULL) |
668 | return (xstrdup(template)); |
669 | |
670 | buf = xmalloc(1); |
671 | *buf = '\0'; |
672 | len = 0; |
673 | replaced = 0; |
674 | |
675 | ptr = template; |
676 | while (*ptr != '\0') { |
677 | switch (ch = *ptr++) { |
678 | case '%': |
679 | if (*ptr < '1' || *ptr > '9' || *ptr - '0' != idx) { |
680 | if (*ptr != '%' || replaced) |
681 | break; |
682 | replaced = 1; |
683 | } |
684 | ptr++; |
685 | |
686 | quoted = (*ptr == '%'); |
687 | if (quoted) |
688 | ptr++; |
689 | |
690 | buf = xrealloc(buf, len + (strlen(s) * 3) + 1); |
691 | for (cp = s; *cp != '\0'; cp++) { |
692 | if (quoted && strchr(quote, *cp) != NULL) |
693 | buf[len++] = '\\'; |
694 | buf[len++] = *cp; |
695 | } |
696 | buf[len] = '\0'; |
697 | continue; |
698 | } |
699 | buf = xrealloc(buf, len + 2); |
700 | buf[len++] = ch; |
701 | buf[len] = '\0'; |
702 | } |
703 | |
704 | return (buf); |
705 | } |
706 | |