cmd.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  1. /* $OpenBSD$ */
  2. /*
  3. * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
  4. *
  5. * Permission to use, copy, modify, and distribute this software for any
  6. * purpose with or without fee is hereby granted, provided that the above
  7. * copyright notice and this permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  10. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  11. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  12. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  13. * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
  14. * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
  15. * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  16. */
  17. #include <sys/types.h>
  18. #include <sys/time.h>
  19. #include <fnmatch.h>
  20. #include <pwd.h>
  21. #include <stdlib.h>
  22. #include <string.h>
  23. #include <unistd.h>
  24. #include "tmux.h"
  25. extern const struct cmd_entry cmd_attach_session_entry;
  26. extern const struct cmd_entry cmd_bind_key_entry;
  27. extern const struct cmd_entry cmd_break_pane_entry;
  28. extern const struct cmd_entry cmd_capture_pane_entry;
  29. extern const struct cmd_entry cmd_choose_buffer_entry;
  30. extern const struct cmd_entry cmd_choose_client_entry;
  31. extern const struct cmd_entry cmd_choose_session_entry;
  32. extern const struct cmd_entry cmd_choose_tree_entry;
  33. extern const struct cmd_entry cmd_choose_window_entry;
  34. extern const struct cmd_entry cmd_clear_history_entry;
  35. extern const struct cmd_entry cmd_clock_mode_entry;
  36. extern const struct cmd_entry cmd_command_prompt_entry;
  37. extern const struct cmd_entry cmd_confirm_before_entry;
  38. extern const struct cmd_entry cmd_copy_mode_entry;
  39. extern const struct cmd_entry cmd_delete_buffer_entry;
  40. extern const struct cmd_entry cmd_detach_client_entry;
  41. extern const struct cmd_entry cmd_display_message_entry;
  42. extern const struct cmd_entry cmd_display_panes_entry;
  43. extern const struct cmd_entry cmd_down_pane_entry;
  44. extern const struct cmd_entry cmd_find_window_entry;
  45. extern const struct cmd_entry cmd_has_session_entry;
  46. extern const struct cmd_entry cmd_if_shell_entry;
  47. extern const struct cmd_entry cmd_join_pane_entry;
  48. extern const struct cmd_entry cmd_kill_pane_entry;
  49. extern const struct cmd_entry cmd_kill_server_entry;
  50. extern const struct cmd_entry cmd_kill_session_entry;
  51. extern const struct cmd_entry cmd_kill_window_entry;
  52. extern const struct cmd_entry cmd_last_pane_entry;
  53. extern const struct cmd_entry cmd_last_window_entry;
  54. extern const struct cmd_entry cmd_link_window_entry;
  55. extern const struct cmd_entry cmd_list_buffers_entry;
  56. extern const struct cmd_entry cmd_list_clients_entry;
  57. extern const struct cmd_entry cmd_list_commands_entry;
  58. extern const struct cmd_entry cmd_list_keys_entry;
  59. extern const struct cmd_entry cmd_list_panes_entry;
  60. extern const struct cmd_entry cmd_list_sessions_entry;
  61. extern const struct cmd_entry cmd_list_windows_entry;
  62. extern const struct cmd_entry cmd_load_buffer_entry;
  63. extern const struct cmd_entry cmd_lock_client_entry;
  64. extern const struct cmd_entry cmd_lock_server_entry;
  65. extern const struct cmd_entry cmd_lock_session_entry;
  66. extern const struct cmd_entry cmd_move_pane_entry;
  67. extern const struct cmd_entry cmd_move_window_entry;
  68. extern const struct cmd_entry cmd_new_session_entry;
  69. extern const struct cmd_entry cmd_new_window_entry;
  70. extern const struct cmd_entry cmd_next_layout_entry;
  71. extern const struct cmd_entry cmd_next_window_entry;
  72. extern const struct cmd_entry cmd_paste_buffer_entry;
  73. extern const struct cmd_entry cmd_pipe_pane_entry;
  74. extern const struct cmd_entry cmd_previous_layout_entry;
  75. extern const struct cmd_entry cmd_previous_window_entry;
  76. extern const struct cmd_entry cmd_refresh_client_entry;
  77. extern const struct cmd_entry cmd_rename_session_entry;
  78. extern const struct cmd_entry cmd_rename_window_entry;
  79. extern const struct cmd_entry cmd_resize_pane_entry;
  80. extern const struct cmd_entry cmd_respawn_pane_entry;
  81. extern const struct cmd_entry cmd_respawn_window_entry;
  82. extern const struct cmd_entry cmd_rotate_window_entry;
  83. extern const struct cmd_entry cmd_run_shell_entry;
  84. extern const struct cmd_entry cmd_save_buffer_entry;
  85. extern const struct cmd_entry cmd_select_layout_entry;
  86. extern const struct cmd_entry cmd_select_pane_entry;
  87. extern const struct cmd_entry cmd_select_window_entry;
  88. extern const struct cmd_entry cmd_send_keys_entry;
  89. extern const struct cmd_entry cmd_send_prefix_entry;
  90. extern const struct cmd_entry cmd_server_info_entry;
  91. extern const struct cmd_entry cmd_set_buffer_entry;
  92. extern const struct cmd_entry cmd_set_environment_entry;
  93. extern const struct cmd_entry cmd_set_hook_entry;
  94. extern const struct cmd_entry cmd_set_option_entry;
  95. extern const struct cmd_entry cmd_set_window_option_entry;
  96. extern const struct cmd_entry cmd_show_buffer_entry;
  97. extern const struct cmd_entry cmd_show_environment_entry;
  98. extern const struct cmd_entry cmd_show_hooks_entry;
  99. extern const struct cmd_entry cmd_show_messages_entry;
  100. extern const struct cmd_entry cmd_show_options_entry;
  101. extern const struct cmd_entry cmd_show_window_options_entry;
  102. extern const struct cmd_entry cmd_source_file_entry;
  103. extern const struct cmd_entry cmd_split_window_entry;
  104. extern const struct cmd_entry cmd_start_server_entry;
  105. extern const struct cmd_entry cmd_suspend_client_entry;
  106. extern const struct cmd_entry cmd_swap_pane_entry;
  107. extern const struct cmd_entry cmd_swap_window_entry;
  108. extern const struct cmd_entry cmd_switch_client_entry;
  109. extern const struct cmd_entry cmd_unbind_key_entry;
  110. extern const struct cmd_entry cmd_unlink_window_entry;
  111. extern const struct cmd_entry cmd_up_pane_entry;
  112. extern const struct cmd_entry cmd_wait_for_entry;
  113. const struct cmd_entry *cmd_table[] = {
  114. &cmd_attach_session_entry,
  115. &cmd_bind_key_entry,
  116. &cmd_break_pane_entry,
  117. &cmd_capture_pane_entry,
  118. &cmd_choose_buffer_entry,
  119. &cmd_choose_client_entry,
  120. &cmd_choose_session_entry,
  121. &cmd_choose_tree_entry,
  122. &cmd_choose_window_entry,
  123. &cmd_clear_history_entry,
  124. &cmd_clock_mode_entry,
  125. &cmd_command_prompt_entry,
  126. &cmd_confirm_before_entry,
  127. &cmd_copy_mode_entry,
  128. &cmd_delete_buffer_entry,
  129. &cmd_detach_client_entry,
  130. &cmd_display_message_entry,
  131. &cmd_display_panes_entry,
  132. &cmd_find_window_entry,
  133. &cmd_has_session_entry,
  134. &cmd_if_shell_entry,
  135. &cmd_join_pane_entry,
  136. &cmd_kill_pane_entry,
  137. &cmd_kill_server_entry,
  138. &cmd_kill_session_entry,
  139. &cmd_kill_window_entry,
  140. &cmd_last_pane_entry,
  141. &cmd_last_window_entry,
  142. &cmd_link_window_entry,
  143. &cmd_list_buffers_entry,
  144. &cmd_list_clients_entry,
  145. &cmd_list_commands_entry,
  146. &cmd_list_keys_entry,
  147. &cmd_list_panes_entry,
  148. &cmd_list_sessions_entry,
  149. &cmd_list_windows_entry,
  150. &cmd_load_buffer_entry,
  151. &cmd_lock_client_entry,
  152. &cmd_lock_server_entry,
  153. &cmd_lock_session_entry,
  154. &cmd_move_pane_entry,
  155. &cmd_move_window_entry,
  156. &cmd_new_session_entry,
  157. &cmd_new_window_entry,
  158. &cmd_next_layout_entry,
  159. &cmd_next_window_entry,
  160. &cmd_paste_buffer_entry,
  161. &cmd_pipe_pane_entry,
  162. &cmd_previous_layout_entry,
  163. &cmd_previous_window_entry,
  164. &cmd_refresh_client_entry,
  165. &cmd_rename_session_entry,
  166. &cmd_rename_window_entry,
  167. &cmd_resize_pane_entry,
  168. &cmd_respawn_pane_entry,
  169. &cmd_respawn_window_entry,
  170. &cmd_rotate_window_entry,
  171. &cmd_run_shell_entry,
  172. &cmd_save_buffer_entry,
  173. &cmd_select_layout_entry,
  174. &cmd_select_pane_entry,
  175. &cmd_select_window_entry,
  176. &cmd_send_keys_entry,
  177. &cmd_send_prefix_entry,
  178. &cmd_server_info_entry,
  179. &cmd_set_buffer_entry,
  180. &cmd_set_environment_entry,
  181. &cmd_set_hook_entry,
  182. &cmd_set_option_entry,
  183. &cmd_set_window_option_entry,
  184. &cmd_show_buffer_entry,
  185. &cmd_show_environment_entry,
  186. &cmd_show_hooks_entry,
  187. &cmd_show_messages_entry,
  188. &cmd_show_options_entry,
  189. &cmd_show_window_options_entry,
  190. &cmd_source_file_entry,
  191. &cmd_split_window_entry,
  192. &cmd_start_server_entry,
  193. &cmd_suspend_client_entry,
  194. &cmd_swap_pane_entry,
  195. &cmd_swap_window_entry,
  196. &cmd_switch_client_entry,
  197. &cmd_unbind_key_entry,
  198. &cmd_unlink_window_entry,
  199. &cmd_wait_for_entry,
  200. NULL
  201. };
  202. int
  203. cmd_pack_argv(int argc, char **argv, char *buf, size_t len)
  204. {
  205. size_t arglen;
  206. int i;
  207. if (argc == 0)
  208. return (0);
  209. *buf = '\0';
  210. for (i = 0; i < argc; i++) {
  211. if (strlcpy(buf, argv[i], len) >= len)
  212. return (-1);
  213. arglen = strlen(argv[i]) + 1;
  214. buf += arglen;
  215. len -= arglen;
  216. }
  217. return (0);
  218. }
  219. int
  220. cmd_unpack_argv(char *buf, size_t len, int argc, char ***argv)
  221. {
  222. int i;
  223. size_t arglen;
  224. if (argc == 0)
  225. return (0);
  226. *argv = xcalloc(argc, sizeof **argv);
  227. buf[len - 1] = '\0';
  228. for (i = 0; i < argc; i++) {
  229. if (len == 0) {
  230. cmd_free_argv(argc, *argv);
  231. return (-1);
  232. }
  233. arglen = strlen(buf) + 1;
  234. (*argv)[i] = xstrdup(buf);
  235. buf += arglen;
  236. len -= arglen;
  237. }
  238. return (0);
  239. }
  240. char **
  241. cmd_copy_argv(int argc, char **argv)
  242. {
  243. char **new_argv;
  244. int i;
  245. if (argc == 0)
  246. return (NULL);
  247. new_argv = xcalloc(argc + 1, sizeof *new_argv);
  248. for (i = 0; i < argc; i++) {
  249. if (argv[i] != NULL)
  250. new_argv[i] = xstrdup(argv[i]);
  251. }
  252. return (new_argv);
  253. }
  254. void
  255. cmd_free_argv(int argc, char **argv)
  256. {
  257. int i;
  258. if (argc == 0)
  259. return;
  260. for (i = 0; i < argc; i++)
  261. free(argv[i]);
  262. free(argv);
  263. }
  264. char *
  265. cmd_stringify_argv(int argc, char **argv)
  266. {
  267. char *buf;
  268. int i;
  269. size_t len;
  270. if (argc == 0)
  271. return (xstrdup(""));
  272. len = 0;
  273. buf = NULL;
  274. for (i = 0; i < argc; i++) {
  275. len += strlen(argv[i]) + 1;
  276. buf = xrealloc(buf, len);
  277. if (i == 0)
  278. *buf = '\0';
  279. else
  280. strlcat(buf, " ", len);
  281. strlcat(buf, argv[i], len);
  282. }
  283. return (buf);
  284. }
  285. struct cmd *
  286. cmd_parse(int argc, char **argv, const char *file, u_int line, char **cause)
  287. {
  288. const struct cmd_entry **entryp, *entry;
  289. struct cmd *cmd;
  290. struct args *args;
  291. char s[BUFSIZ];
  292. int ambiguous = 0;
  293. *cause = NULL;
  294. if (argc == 0) {
  295. xasprintf(cause, "no command");
  296. return (NULL);
  297. }
  298. entry = NULL;
  299. for (entryp = cmd_table; *entryp != NULL; entryp++) {
  300. if ((*entryp)->alias != NULL &&
  301. strcmp((*entryp)->alias, argv[0]) == 0) {
  302. ambiguous = 0;
  303. entry = *entryp;
  304. break;
  305. }
  306. if (strncmp((*entryp)->name, argv[0], strlen(argv[0])) != 0)
  307. continue;
  308. if (entry != NULL)
  309. ambiguous = 1;
  310. entry = *entryp;
  311. /* Bail now if an exact match. */
  312. if (strcmp(entry->name, argv[0]) == 0)
  313. break;
  314. }
  315. if (ambiguous)
  316. goto ambiguous;
  317. if (entry == NULL) {
  318. xasprintf(cause, "unknown command: %s", argv[0]);
  319. return (NULL);
  320. }
  321. args = args_parse(entry->args.template, argc, argv);
  322. if (args == NULL)
  323. goto usage;
  324. if (entry->args.lower != -1 && args->argc < entry->args.lower)
  325. goto usage;
  326. if (entry->args.upper != -1 && args->argc > entry->args.upper)
  327. goto usage;
  328. cmd = xcalloc(1, sizeof *cmd);
  329. cmd->entry = entry;
  330. cmd->args = args;
  331. if (file != NULL)
  332. cmd->file = xstrdup(file);
  333. cmd->line = line;
  334. return (cmd);
  335. ambiguous:
  336. *s = '\0';
  337. for (entryp = cmd_table; *entryp != NULL; entryp++) {
  338. if (strncmp((*entryp)->name, argv[0], strlen(argv[0])) != 0)
  339. continue;
  340. if (strlcat(s, (*entryp)->name, sizeof s) >= sizeof s)
  341. break;
  342. if (strlcat(s, ", ", sizeof s) >= sizeof s)
  343. break;
  344. }
  345. s[strlen(s) - 2] = '\0';
  346. xasprintf(cause, "ambiguous command: %s, could be: %s", argv[0], s);
  347. return (NULL);
  348. usage:
  349. if (args != NULL)
  350. args_free(args);
  351. xasprintf(cause, "usage: %s %s", entry->name, entry->usage);
  352. return (NULL);
  353. }
  354. static int
  355. cmd_prepare_state_flag(char c, const char *target, enum cmd_entry_flag flag,
  356. struct cmd_q *cmdq, struct cmd_q *parent)
  357. {
  358. int targetflags, error;
  359. struct cmd_find_state *fs = NULL;
  360. struct cmd_find_state *current = NULL;
  361. struct cmd_find_state tmp;
  362. if (flag == CMD_NONE ||
  363. flag == CMD_CLIENT ||
  364. flag == CMD_CLIENT_CANFAIL)
  365. return (0);
  366. if (c == 't')
  367. fs = &cmdq->state.tflag;
  368. else if (c == 's')
  369. fs = &cmdq->state.sflag;
  370. if (flag == CMD_SESSION_WITHPANE) {
  371. if (target != NULL && target[strcspn(target, ":.")] != '\0')
  372. flag = CMD_PANE;
  373. else
  374. flag = CMD_SESSION;
  375. }
  376. targetflags = 0;
  377. switch (flag) {
  378. case CMD_SESSION:
  379. case CMD_SESSION_CANFAIL:
  380. case CMD_SESSION_PREFERUNATTACHED:
  381. if (flag == CMD_SESSION_CANFAIL)
  382. targetflags |= CMD_FIND_QUIET;
  383. if (flag == CMD_SESSION_PREFERUNATTACHED)
  384. targetflags |= CMD_FIND_PREFER_UNATTACHED;
  385. break;
  386. case CMD_MOVEW_R:
  387. flag = CMD_WINDOW_INDEX;
  388. /* FALLTHROUGH */
  389. case CMD_WINDOW:
  390. case CMD_WINDOW_CANFAIL:
  391. case CMD_WINDOW_MARKED:
  392. case CMD_WINDOW_INDEX:
  393. if (flag == CMD_WINDOW_CANFAIL)
  394. targetflags |= CMD_FIND_QUIET;
  395. if (flag == CMD_WINDOW_MARKED)
  396. targetflags |= CMD_FIND_DEFAULT_MARKED;
  397. if (flag == CMD_WINDOW_INDEX)
  398. targetflags |= CMD_FIND_WINDOW_INDEX;
  399. break;
  400. case CMD_PANE:
  401. case CMD_PANE_CANFAIL:
  402. case CMD_PANE_MARKED:
  403. if (flag == CMD_PANE_CANFAIL)
  404. targetflags |= CMD_FIND_QUIET;
  405. if (flag == CMD_PANE_MARKED)
  406. targetflags |= CMD_FIND_DEFAULT_MARKED;
  407. break;
  408. default:
  409. fatalx("unknown %cflag %d", c, flag);
  410. }
  411. log_debug("%s: flag %c %d %#x", __func__, c, flag, targetflags);
  412. if (parent != NULL) {
  413. if (c == 't')
  414. current = &parent->state.tflag;
  415. else if (c == 's')
  416. current = &parent->state.sflag;
  417. } else {
  418. error = cmd_find_current(&tmp, cmdq, targetflags);
  419. if (error != 0 && ~targetflags & CMD_FIND_QUIET)
  420. return (-1);
  421. current = &tmp;
  422. }
  423. switch (flag) {
  424. case CMD_NONE:
  425. case CMD_CLIENT:
  426. case CMD_CLIENT_CANFAIL:
  427. return (0);
  428. case CMD_SESSION:
  429. case CMD_SESSION_CANFAIL:
  430. case CMD_SESSION_PREFERUNATTACHED:
  431. case CMD_SESSION_WITHPANE:
  432. error = cmd_find_target(fs, current, cmdq, target,
  433. CMD_FIND_SESSION, targetflags);
  434. if (error != 0 && ~targetflags & CMD_FIND_QUIET)
  435. return (-1);
  436. break;
  437. case CMD_MOVEW_R:
  438. error = cmd_find_target(fs, current, cmdq, target,
  439. CMD_FIND_SESSION, CMD_FIND_QUIET);
  440. if (error == 0)
  441. break;
  442. /* FALLTHROUGH */
  443. case CMD_WINDOW:
  444. case CMD_WINDOW_CANFAIL:
  445. case CMD_WINDOW_MARKED:
  446. case CMD_WINDOW_INDEX:
  447. error = cmd_find_target(fs, current, cmdq, target,
  448. CMD_FIND_WINDOW, targetflags);
  449. if (error != 0 && ~targetflags & CMD_FIND_QUIET)
  450. return (-1);
  451. break;
  452. case CMD_PANE:
  453. case CMD_PANE_CANFAIL:
  454. case CMD_PANE_MARKED:
  455. error = cmd_find_target(fs, current, cmdq, target,
  456. CMD_FIND_PANE, targetflags);
  457. if (error != 0 && ~targetflags & CMD_FIND_QUIET)
  458. return (-1);
  459. break;
  460. default:
  461. fatalx("unknown %cflag %d", c, flag);
  462. }
  463. return (0);
  464. }
  465. int
  466. cmd_prepare_state(struct cmd *cmd, struct cmd_q *cmdq, struct cmd_q *parent)
  467. {
  468. const struct cmd_entry *entry = cmd->entry;
  469. struct cmd_state *state = &cmdq->state;
  470. char *tmp;
  471. enum cmd_entry_flag flag;
  472. const char *s;
  473. int error;
  474. tmp = cmd_print(cmd);
  475. log_debug("preparing state for %s (client %p)", tmp, cmdq->client);
  476. free(tmp);
  477. state->c = NULL;
  478. cmd_find_clear_state(&state->tflag, NULL, 0);
  479. cmd_find_clear_state(&state->sflag, NULL, 0);
  480. flag = cmd->entry->cflag;
  481. if (flag == CMD_NONE) {
  482. flag = cmd->entry->tflag;
  483. if (flag == CMD_CLIENT || flag == CMD_CLIENT_CANFAIL)
  484. s = args_get(cmd->args, 't');
  485. else
  486. s = NULL;
  487. } else
  488. s = args_get(cmd->args, 'c');
  489. switch (flag) {
  490. case CMD_CLIENT:
  491. state->c = cmd_find_client(cmdq, s, 0);
  492. if (state->c == NULL)
  493. return (-1);
  494. break;
  495. default:
  496. state->c = cmd_find_client(cmdq, s, 1);
  497. break;
  498. }
  499. s = args_get(cmd->args, 't');
  500. log_debug("preparing -t state: target %s", s == NULL ? "none" : s);
  501. error = cmd_prepare_state_flag('t', s, entry->tflag, cmdq, parent);
  502. if (error != 0)
  503. return (error);
  504. s = args_get(cmd->args, 's');
  505. log_debug("preparing -s state: target %s", s == NULL ? "none" : s);
  506. error = cmd_prepare_state_flag('s', s, entry->sflag, cmdq, parent);
  507. if (error != 0)
  508. return (error);
  509. return (0);
  510. }
  511. char *
  512. cmd_print(struct cmd *cmd)
  513. {
  514. char *out, *s;
  515. s = args_print(cmd->args);
  516. if (*s != '\0')
  517. xasprintf(&out, "%s %s", cmd->entry->name, s);
  518. else
  519. out = xstrdup(cmd->entry->name);
  520. free(s);
  521. return (out);
  522. }
  523. /* Adjust current mouse position for a pane. */
  524. int
  525. cmd_mouse_at(struct window_pane *wp, struct mouse_event *m, u_int *xp,
  526. u_int *yp, int last)
  527. {
  528. u_int x, y;
  529. if (last) {
  530. x = m->lx;
  531. y = m->ly;
  532. } else {
  533. x = m->x;
  534. y = m->y;
  535. }
  536. if (m->statusat == 0 && y > 0)
  537. y--;
  538. else if (m->statusat > 0 && y >= (u_int)m->statusat)
  539. y = m->statusat - 1;
  540. if (x < wp->xoff || x >= wp->xoff + wp->sx)
  541. return (-1);
  542. if (y < wp->yoff || y >= wp->yoff + wp->sy)
  543. return (-1);
  544. *xp = x - wp->xoff;
  545. *yp = y - wp->yoff;
  546. return (0);
  547. }
  548. /* Get current mouse window if any. */
  549. struct winlink *
  550. cmd_mouse_window(struct mouse_event *m, struct session **sp)
  551. {
  552. struct session *s;
  553. struct window *w;
  554. if (!m->valid || m->s == -1 || m->w == -1)
  555. return (NULL);
  556. if ((s = session_find_by_id(m->s)) == NULL)
  557. return (NULL);
  558. if ((w = window_find_by_id(m->w)) == NULL)
  559. return (NULL);
  560. if (sp != NULL)
  561. *sp = s;
  562. return (winlink_find_by_window(&s->windows, w));
  563. }
  564. /* Get current mouse pane if any. */
  565. struct window_pane *
  566. cmd_mouse_pane(struct mouse_event *m, struct session **sp,
  567. struct winlink **wlp)
  568. {
  569. struct winlink *wl;
  570. struct window_pane *wp;
  571. if ((wl = cmd_mouse_window(m, sp)) == NULL)
  572. return (NULL);
  573. if ((wp = window_pane_find_by_id(m->wp)) == NULL)
  574. return (NULL);
  575. if (!window_has_pane(wl->window, wp))
  576. return (NULL);
  577. if (wlp != NULL)
  578. *wlp = wl;
  579. return (wp);
  580. }
  581. /* Replace the first %% or %idx in template by s. */
  582. char *
  583. cmd_template_replace(const char *template, const char *s, int idx)
  584. {
  585. char ch, *buf;
  586. const char *ptr;
  587. int replaced;
  588. size_t len;
  589. if (strchr(template, '%') == NULL)
  590. return (xstrdup(template));
  591. buf = xmalloc(1);
  592. *buf = '\0';
  593. len = 0;
  594. replaced = 0;
  595. ptr = template;
  596. while (*ptr != '\0') {
  597. switch (ch = *ptr++) {
  598. case '%':
  599. if (*ptr < '1' || *ptr > '9' || *ptr - '0' != idx) {
  600. if (*ptr != '%' || replaced)
  601. break;
  602. replaced = 1;
  603. }
  604. ptr++;
  605. len += strlen(s);
  606. buf = xrealloc(buf, len + 1);
  607. strlcat(buf, s, len + 1);
  608. continue;
  609. }
  610. buf = xrealloc(buf, len + 2);
  611. buf[len++] = ch;
  612. buf[len] = '\0';
  613. }
  614. return (buf);
  615. }