cmd-find.c 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257
  1. /* $OpenBSD$ */
  2. /*
  3. * Copyright (c) 2015 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 <fnmatch.h>
  19. #include <limits.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include <unistd.h>
  23. #include "tmux.h"
  24. struct session *cmd_find_try_TMUX(struct client *, struct window *);
  25. int cmd_find_client_better(struct client *, struct client *);
  26. struct client *cmd_find_best_client(struct client **, u_int);
  27. int cmd_find_session_better(struct session *, struct session *,
  28. int);
  29. struct session *cmd_find_best_session(struct session **, u_int, int);
  30. int cmd_find_best_session_with_window(struct cmd_find_state *);
  31. int cmd_find_best_winlink_with_window(struct cmd_find_state *);
  32. int cmd_find_current_session_with_client(struct cmd_find_state *);
  33. int cmd_find_current_session(struct cmd_find_state *);
  34. struct client *cmd_find_current_client(struct cmd_q *);
  35. const char *cmd_find_map_table(const char *[][2], const char *);
  36. int cmd_find_get_session(struct cmd_find_state *, const char *);
  37. int cmd_find_get_window(struct cmd_find_state *, const char *);
  38. int cmd_find_get_window_with_session(struct cmd_find_state *, const char *);
  39. int cmd_find_get_window_with_pane(struct cmd_find_state *);
  40. int cmd_find_get_pane(struct cmd_find_state *, const char *);
  41. int cmd_find_get_pane_with_session(struct cmd_find_state *, const char *);
  42. int cmd_find_get_pane_with_window(struct cmd_find_state *, const char *);
  43. const char *cmd_find_session_table[][2] = {
  44. { NULL, NULL }
  45. };
  46. const char *cmd_find_window_table[][2] = {
  47. { "{start}", "^" },
  48. { "{last}", "!" },
  49. { "{end}", "$" },
  50. { "{next}", "+" },
  51. { "{previous}", "-" },
  52. { NULL, NULL }
  53. };
  54. const char *cmd_find_pane_table[][2] = {
  55. { "{last}", "!" },
  56. { "{next}", "+" },
  57. { "{previous}", "-" },
  58. { "{top}", "top" },
  59. { "{bottom}", "bottom" },
  60. { "{left}", "left" },
  61. { "{right}", "right" },
  62. { "{top-left}", "top-left" },
  63. { "{top-right}", "top-right" },
  64. { "{bottom-left}", "bottom-left" },
  65. { "{bottom-right}", "bottom-right" },
  66. { "{up-of}", "{up-of}" },
  67. { "{down-of}", "{down-of}" },
  68. { "{left-of}", "{left-of}" },
  69. { "{right-of}", "{right-of}" },
  70. { NULL, NULL }
  71. };
  72. /* Get session from TMUX if present. */
  73. struct session *
  74. cmd_find_try_TMUX(struct client *c, struct window *w)
  75. {
  76. struct environ_entry *envent;
  77. char tmp[256];
  78. long long pid;
  79. u_int session;
  80. struct session *s;
  81. envent = environ_find(c->environ, "TMUX");
  82. if (envent == NULL)
  83. return (NULL);
  84. if (sscanf(envent->value, "%255[^,],%lld,%d", tmp, &pid, &session) != 3)
  85. return (NULL);
  86. if (pid != getpid())
  87. return (NULL);
  88. log_debug("client %p TMUX is %s (session @%u)", c, envent->value,
  89. session);
  90. s = session_find_by_id(session);
  91. if (s == NULL || (w != NULL && !session_has(s, w)))
  92. return (NULL);
  93. return (s);
  94. }
  95. /* Is this client better? */
  96. int
  97. cmd_find_client_better(struct client *c, struct client *than)
  98. {
  99. if (than == NULL)
  100. return (1);
  101. return (timercmp(&c->activity_time, &than->activity_time, >));
  102. }
  103. /* Find best client from a list, or all if list is NULL. */
  104. struct client *
  105. cmd_find_best_client(struct client **clist, u_int csize)
  106. {
  107. struct client *c_loop, *c;
  108. u_int i;
  109. c = NULL;
  110. if (clist != NULL) {
  111. for (i = 0; i < csize; i++) {
  112. if (clist[i]->session == NULL)
  113. continue;
  114. if (cmd_find_client_better(clist[i], c))
  115. c = clist[i];
  116. }
  117. } else {
  118. TAILQ_FOREACH(c_loop, &clients, entry) {
  119. if (c_loop->session == NULL)
  120. continue;
  121. if (cmd_find_client_better(c_loop, c))
  122. c = c_loop;
  123. }
  124. }
  125. return (c);
  126. }
  127. /* Is this session better? */
  128. int
  129. cmd_find_session_better(struct session *s, struct session *than, int flags)
  130. {
  131. int attached;
  132. if (than == NULL)
  133. return (1);
  134. if (flags & CMD_FIND_PREFER_UNATTACHED) {
  135. attached = (~than->flags & SESSION_UNATTACHED);
  136. if (attached && (s->flags & SESSION_UNATTACHED))
  137. return (1);
  138. else if (!attached && (~s->flags & SESSION_UNATTACHED))
  139. return (0);
  140. }
  141. return (timercmp(&s->activity_time, &than->activity_time, >));
  142. }
  143. /* Find best session from a list, or all if list is NULL. */
  144. struct session *
  145. cmd_find_best_session(struct session **slist, u_int ssize, int flags)
  146. {
  147. struct session *s_loop, *s;
  148. u_int i;
  149. s = NULL;
  150. if (slist != NULL) {
  151. for (i = 0; i < ssize; i++) {
  152. if (cmd_find_session_better(slist[i], s, flags))
  153. s = slist[i];
  154. }
  155. } else {
  156. RB_FOREACH(s_loop, sessions, &sessions) {
  157. if (cmd_find_session_better(s_loop, s, flags))
  158. s = s_loop;
  159. }
  160. }
  161. return (s);
  162. }
  163. /* Find best session and winlink for window. */
  164. int
  165. cmd_find_best_session_with_window(struct cmd_find_state *fs)
  166. {
  167. struct session **slist = NULL;
  168. u_int ssize;
  169. struct session *s;
  170. if (fs->cmdq != NULL && fs->cmdq->client != NULL) {
  171. fs->s = cmd_find_try_TMUX(fs->cmdq->client, fs->w);
  172. if (fs->s != NULL)
  173. return (cmd_find_best_winlink_with_window(fs));
  174. }
  175. ssize = 0;
  176. RB_FOREACH(s, sessions, &sessions) {
  177. if (!session_has(s, fs->w))
  178. continue;
  179. slist = xreallocarray(slist, ssize + 1, sizeof *slist);
  180. slist[ssize++] = s;
  181. }
  182. if (ssize == 0)
  183. goto fail;
  184. fs->s = cmd_find_best_session(slist, ssize, fs->flags);
  185. if (fs->s == NULL)
  186. goto fail;
  187. free(slist);
  188. return (cmd_find_best_winlink_with_window(fs));
  189. fail:
  190. free(slist);
  191. return (-1);
  192. }
  193. /*
  194. * Find the best winlink for a window (the current if it contains the pane,
  195. * otherwise the first).
  196. */
  197. int
  198. cmd_find_best_winlink_with_window(struct cmd_find_state *fs)
  199. {
  200. struct winlink *wl, *wl_loop;
  201. wl = NULL;
  202. if (fs->s->curw->window == fs->w)
  203. wl = fs->s->curw;
  204. else {
  205. RB_FOREACH(wl_loop, winlinks, &fs->s->windows) {
  206. if (wl_loop->window == fs->w) {
  207. wl = wl_loop;
  208. break;
  209. }
  210. }
  211. }
  212. if (wl == NULL)
  213. return (-1);
  214. fs->wl = wl;
  215. fs->idx = fs->wl->idx;
  216. return (0);
  217. }
  218. /* Find current session when we have an unattached client. */
  219. int
  220. cmd_find_current_session_with_client(struct cmd_find_state *fs)
  221. {
  222. struct window_pane *wp;
  223. /*
  224. * If this is running in a pane, we can use that to limit the list of
  225. * sessions to those containing that pane (we still use the current
  226. * window in the best session).
  227. */
  228. if (fs->cmdq != NULL && fs->cmdq->client->tty.path != NULL) {
  229. RB_FOREACH(wp, window_pane_tree, &all_window_panes) {
  230. if (strcmp(wp->tty, fs->cmdq->client->tty.path) == 0)
  231. break;
  232. }
  233. } else
  234. wp = NULL;
  235. /* Not running in a pane. We know nothing. Find the best session. */
  236. if (wp == NULL)
  237. goto unknown_pane;
  238. /* Find the best session and winlink containing this pane. */
  239. fs->w = wp->window;
  240. if (cmd_find_best_session_with_window(fs) != 0) {
  241. if (wp != NULL) {
  242. /*
  243. * The window may have been destroyed but the pane
  244. * still on all_window_panes due to something else
  245. * holding a reference.
  246. */
  247. goto unknown_pane;
  248. }
  249. return (-1);
  250. }
  251. /* Use the current window and pane from this session. */
  252. fs->wl = fs->s->curw;
  253. fs->idx = fs->wl->idx;
  254. fs->w = fs->wl->window;
  255. fs->wp = fs->w->active;
  256. return (0);
  257. unknown_pane:
  258. fs->s = NULL;
  259. if (fs->cmdq != NULL)
  260. fs->s = cmd_find_try_TMUX(fs->cmdq->client, NULL);
  261. if (fs->s == NULL)
  262. fs->s = cmd_find_best_session(NULL, 0, fs->flags);
  263. if (fs->s == NULL)
  264. return (-1);
  265. fs->wl = fs->s->curw;
  266. fs->idx = fs->wl->idx;
  267. fs->w = fs->wl->window;
  268. fs->wp = fs->w->active;
  269. return (0);
  270. }
  271. /*
  272. * Work out the best current state. If this function succeeds, the state is
  273. * guaranteed to be completely filled in.
  274. */
  275. int
  276. cmd_find_current_session(struct cmd_find_state *fs)
  277. {
  278. /* If we know the current client, use it. */
  279. if (fs->cmdq != NULL && fs->cmdq->client != NULL) {
  280. log_debug("%s: have client %p%s", __func__, fs->cmdq->client,
  281. fs->cmdq->client->session == NULL ? "" : " (with session)");
  282. if (fs->cmdq->client->session == NULL)
  283. return (cmd_find_current_session_with_client(fs));
  284. fs->s = fs->cmdq->client->session;
  285. fs->wl = fs->s->curw;
  286. fs->idx = fs->wl->idx;
  287. fs->w = fs->wl->window;
  288. fs->wp = fs->w->active;
  289. return (0);
  290. }
  291. /* We know nothing, find the best session and client. */
  292. fs->s = cmd_find_best_session(NULL, 0, fs->flags);
  293. if (fs->s == NULL)
  294. return (-1);
  295. fs->wl = fs->s->curw;
  296. fs->idx = fs->wl->idx;
  297. fs->w = fs->wl->window;
  298. fs->wp = fs->w->active;
  299. return (0);
  300. }
  301. /* Work out the best current client. */
  302. struct client *
  303. cmd_find_current_client(struct cmd_q *cmdq)
  304. {
  305. struct cmd_find_state current;
  306. struct session *s;
  307. struct client *c, **clist = NULL;
  308. u_int csize;
  309. /* If the queue client has a session, use it. */
  310. if (cmdq->client != NULL && cmdq->client->session != NULL) {
  311. log_debug("%s: using cmdq %p client %p", __func__, cmdq,
  312. cmdq->client);
  313. return (cmdq->client);
  314. }
  315. /* Otherwise find the current session. */
  316. cmd_find_clear_state(&current, cmdq, 0);
  317. if (cmd_find_current_session(&current) != 0)
  318. return (NULL);
  319. /* If it is attached, find the best of it's clients. */
  320. s = current.s;
  321. log_debug("%s: current session $%u %s", __func__, s->id, s->name);
  322. if (~s->flags & SESSION_UNATTACHED) {
  323. csize = 0;
  324. TAILQ_FOREACH(c, &clients, entry) {
  325. if (c->session != s)
  326. continue;
  327. clist = xreallocarray(clist, csize + 1, sizeof *clist);
  328. clist[csize++] = c;
  329. }
  330. if (csize != 0) {
  331. c = cmd_find_best_client(clist, csize);
  332. if (c != NULL) {
  333. free(clist);
  334. return (c);
  335. }
  336. }
  337. free(clist);
  338. }
  339. /* Otherwise pick best of all clients. */
  340. return (cmd_find_best_client(NULL, 0));
  341. }
  342. /* Maps string in table. */
  343. const char *
  344. cmd_find_map_table(const char *table[][2], const char *s)
  345. {
  346. u_int i;
  347. for (i = 0; table[i][0] != NULL; i++) {
  348. if (strcmp(s, table[i][0]) == 0)
  349. return (table[i][1]);
  350. }
  351. return (s);
  352. }
  353. /* Find session from string. Fills in s. */
  354. int
  355. cmd_find_get_session(struct cmd_find_state *fs, const char *session)
  356. {
  357. struct session *s, *s_loop;
  358. struct client *c;
  359. log_debug("%s: %s", __func__, session);
  360. /* Check for session ids starting with $. */
  361. if (*session == '$') {
  362. fs->s = session_find_by_id_str(session);
  363. if (fs->s == NULL)
  364. return (-1);
  365. return (0);
  366. }
  367. /* Look for exactly this session. */
  368. fs->s = session_find(session);
  369. if (fs->s != NULL)
  370. return (0);
  371. /* Look for as a client. */
  372. c = cmd_find_client(NULL, session, 1);
  373. if (c != NULL && c->session != NULL) {
  374. fs->s = c->session;
  375. return (0);
  376. }
  377. /* Stop now if exact only. */
  378. if (fs->flags & CMD_FIND_EXACT_SESSION)
  379. return (-1);
  380. /* Otherwise look for prefix. */
  381. s = NULL;
  382. RB_FOREACH(s_loop, sessions, &sessions) {
  383. if (strncmp(session, s_loop->name, strlen(session)) == 0) {
  384. if (s != NULL)
  385. return (-1);
  386. s = s_loop;
  387. }
  388. }
  389. if (s != NULL) {
  390. fs->s = s;
  391. return (0);
  392. }
  393. /* Then as a pattern. */
  394. s = NULL;
  395. RB_FOREACH(s_loop, sessions, &sessions) {
  396. if (fnmatch(session, s_loop->name, 0) == 0) {
  397. if (s != NULL)
  398. return (-1);
  399. s = s_loop;
  400. }
  401. }
  402. if (s != NULL) {
  403. fs->s = s;
  404. return (0);
  405. }
  406. return (-1);
  407. }
  408. /* Find window from string. Fills in s, wl, w. */
  409. int
  410. cmd_find_get_window(struct cmd_find_state *fs, const char *window)
  411. {
  412. log_debug("%s: %s", __func__, window);
  413. /* Check for window ids starting with @. */
  414. if (*window == '@') {
  415. fs->w = window_find_by_id_str(window);
  416. if (fs->w == NULL)
  417. return (-1);
  418. return (cmd_find_best_session_with_window(fs));
  419. }
  420. /* Not a window id, so use the current session. */
  421. fs->s = fs->current->s;
  422. /* We now only need to find the winlink in this session. */
  423. if (cmd_find_get_window_with_session(fs, window) == 0)
  424. return (0);
  425. /* Otherwise try as a session itself. */
  426. if (cmd_find_get_session(fs, window) == 0) {
  427. fs->wl = fs->s->curw;
  428. fs->w = fs->wl->window;
  429. if (~fs->flags & CMD_FIND_WINDOW_INDEX)
  430. fs->idx = fs->wl->idx;
  431. return (0);
  432. }
  433. return (-1);
  434. }
  435. /*
  436. * Find window from string, assuming it is in given session. Needs s, fills in
  437. * wl and w.
  438. */
  439. int
  440. cmd_find_get_window_with_session(struct cmd_find_state *fs, const char *window)
  441. {
  442. struct winlink *wl;
  443. const char *errstr;
  444. int idx, n, exact;
  445. struct session *s;
  446. log_debug("%s: %s", __func__, window);
  447. exact = (fs->flags & CMD_FIND_EXACT_WINDOW);
  448. /*
  449. * Start with the current window as the default. So if only an index is
  450. * found, the window will be the current.
  451. */
  452. fs->wl = fs->s->curw;
  453. fs->w = fs->wl->window;
  454. /* Check for window ids starting with @. */
  455. if (*window == '@') {
  456. fs->w = window_find_by_id_str(window);
  457. if (fs->w == NULL || !session_has(fs->s, fs->w))
  458. return (-1);
  459. return (cmd_find_best_winlink_with_window(fs));
  460. }
  461. /* Try as an offset. */
  462. if (!exact && (window[0] == '+' || window[0] == '-')) {
  463. if (window[1] != '\0')
  464. n = strtonum(window + 1, 1, INT_MAX, NULL);
  465. else
  466. n = 1;
  467. s = fs->s;
  468. if (fs->flags & CMD_FIND_WINDOW_INDEX) {
  469. if (window[0] == '+') {
  470. if (INT_MAX - s->curw->idx < n)
  471. return (-1);
  472. fs->idx = s->curw->idx + n;
  473. } else {
  474. if (n < s->curw->idx)
  475. return (-1);
  476. fs->idx = s->curw->idx - n;
  477. }
  478. return (0);
  479. }
  480. if (window[0] == '+')
  481. fs->wl = winlink_next_by_number(s->curw, s, n);
  482. else
  483. fs->wl = winlink_previous_by_number(s->curw, s, n);
  484. if (fs->wl != NULL) {
  485. fs->idx = fs->wl->idx;
  486. fs->w = fs->wl->window;
  487. return (0);
  488. }
  489. }
  490. /* Try special characters. */
  491. if (!exact) {
  492. if (strcmp(window, "!") == 0) {
  493. fs->wl = TAILQ_FIRST(&fs->s->lastw);
  494. if (fs->wl == NULL)
  495. return (-1);
  496. fs->idx = fs->wl->idx;
  497. fs->w = fs->wl->window;
  498. return (0);
  499. } else if (strcmp(window, "^") == 0) {
  500. fs->wl = RB_MIN(winlinks, &fs->s->windows);
  501. if (fs->wl == NULL)
  502. return (-1);
  503. fs->idx = fs->wl->idx;
  504. fs->w = fs->wl->window;
  505. return (0);
  506. } else if (strcmp(window, "$") == 0) {
  507. fs->wl = RB_MAX(winlinks, &fs->s->windows);
  508. if (fs->wl == NULL)
  509. return (-1);
  510. fs->idx = fs->wl->idx;
  511. fs->w = fs->wl->window;
  512. return (0);
  513. }
  514. }
  515. /* First see if this is a valid window index in this session. */
  516. if (window[0] != '+' && window[0] != '-') {
  517. idx = strtonum(window, 0, INT_MAX, &errstr);
  518. if (errstr == NULL) {
  519. if (fs->flags & CMD_FIND_WINDOW_INDEX) {
  520. fs->idx = idx;
  521. return (0);
  522. }
  523. fs->wl = winlink_find_by_index(&fs->s->windows, idx);
  524. if (fs->wl != NULL) {
  525. fs->w = fs->wl->window;
  526. return (0);
  527. }
  528. }
  529. }
  530. /* Look for exact matches, error if more than one. */
  531. fs->wl = NULL;
  532. RB_FOREACH(wl, winlinks, &fs->s->windows) {
  533. if (strcmp(window, wl->window->name) == 0) {
  534. if (fs->wl != NULL)
  535. return (-1);
  536. fs->wl = wl;
  537. }
  538. }
  539. if (fs->wl != NULL) {
  540. fs->idx = fs->wl->idx;
  541. fs->w = fs->wl->window;
  542. return (0);
  543. }
  544. /* Stop now if exact only. */
  545. if (exact)
  546. return (-1);
  547. /* Try as the start of a window name, error if multiple. */
  548. fs->wl = NULL;
  549. RB_FOREACH(wl, winlinks, &fs->s->windows) {
  550. if (strncmp(window, wl->window->name, strlen(window)) == 0) {
  551. if (fs->wl != NULL)
  552. return (-1);
  553. fs->wl = wl;
  554. }
  555. }
  556. if (fs->wl != NULL) {
  557. fs->idx = fs->wl->idx;
  558. fs->w = fs->wl->window;
  559. return (0);
  560. }
  561. /* Now look for pattern matches, again error if multiple. */
  562. fs->wl = NULL;
  563. RB_FOREACH(wl, winlinks, &fs->s->windows) {
  564. if (fnmatch(window, wl->window->name, 0) == 0) {
  565. if (fs->wl != NULL)
  566. return (-1);
  567. fs->wl = wl;
  568. }
  569. }
  570. if (fs->wl != NULL) {
  571. fs->idx = fs->wl->idx;
  572. fs->w = fs->wl->window;
  573. return (0);
  574. }
  575. return (-1);
  576. }
  577. /* Find window from given pane. Needs wp, fills in s and wl and w. */
  578. int
  579. cmd_find_get_window_with_pane(struct cmd_find_state *fs)
  580. {
  581. log_debug("%s", __func__);
  582. fs->w = fs->wp->window;
  583. return (cmd_find_best_session_with_window(fs));
  584. }
  585. /* Find pane from string. Fills in s, wl, w, wp. */
  586. int
  587. cmd_find_get_pane(struct cmd_find_state *fs, const char *pane)
  588. {
  589. log_debug("%s: %s", __func__, pane);
  590. /* Check for pane ids starting with %. */
  591. if (*pane == '%') {
  592. fs->wp = window_pane_find_by_id_str(pane);
  593. if (fs->wp == NULL)
  594. return (-1);
  595. fs->w = fs->wp->window;
  596. return (cmd_find_best_session_with_window(fs));
  597. }
  598. /* Not a pane id, so try the current session and window. */
  599. fs->s = fs->current->s;
  600. fs->wl = fs->current->wl;
  601. fs->idx = fs->current->idx;
  602. fs->w = fs->current->w;
  603. /* We now only need to find the pane in this window. */
  604. if (cmd_find_get_pane_with_window(fs, pane) == 0)
  605. return (0);
  606. /* Otherwise try as a window itself (this will also try as session). */
  607. if (cmd_find_get_window(fs, pane) == 0) {
  608. fs->wp = fs->w->active;
  609. return (0);
  610. }
  611. return (-1);
  612. }
  613. /*
  614. * Find pane from string, assuming it is in given session. Needs s, fills in wl
  615. * and w and wp.
  616. */
  617. int
  618. cmd_find_get_pane_with_session(struct cmd_find_state *fs, const char *pane)
  619. {
  620. log_debug("%s: %s", __func__, pane);
  621. /* Check for pane ids starting with %. */
  622. if (*pane == '%') {
  623. fs->wp = window_pane_find_by_id_str(pane);
  624. if (fs->wp == NULL)
  625. return (-1);
  626. fs->w = fs->wp->window;
  627. return (cmd_find_best_winlink_with_window(fs));
  628. }
  629. /* Otherwise use the current window. */
  630. fs->wl = fs->s->curw;
  631. fs->idx = fs->wl->idx;
  632. fs->w = fs->wl->window;
  633. /* Now we just need to look up the pane. */
  634. return (cmd_find_get_pane_with_window(fs, pane));
  635. }
  636. /*
  637. * Find pane from string, assuming it is in the given window. Needs w, fills in
  638. * wp.
  639. */
  640. int
  641. cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane)
  642. {
  643. const char *errstr;
  644. int idx;
  645. struct window_pane *wp;
  646. u_int n;
  647. log_debug("%s: %s", __func__, pane);
  648. /* Check for pane ids starting with %. */
  649. if (*pane == '%') {
  650. fs->wp = window_pane_find_by_id_str(pane);
  651. if (fs->wp == NULL || fs->wp->window != fs->w)
  652. return (-1);
  653. return (0);
  654. }
  655. /* Try special characters. */
  656. if (strcmp(pane, "!") == 0) {
  657. if (fs->w->last == NULL)
  658. return (-1);
  659. fs->wp = fs->w->last;
  660. return (0);
  661. } else if (strcmp(pane, "{up-of}") == 0) {
  662. fs->wp = window_pane_find_up(fs->w->active);
  663. if (fs->wp == NULL)
  664. return (-1);
  665. return (0);
  666. } else if (strcmp(pane, "{down-of}") == 0) {
  667. fs->wp = window_pane_find_down(fs->w->active);
  668. if (fs->wp == NULL)
  669. return (-1);
  670. return (0);
  671. } else if (strcmp(pane, "{left-of}") == 0) {
  672. fs->wp = window_pane_find_left(fs->w->active);
  673. if (fs->wp == NULL)
  674. return (-1);
  675. return (0);
  676. } else if (strcmp(pane, "{right-of}") == 0) {
  677. fs->wp = window_pane_find_right(fs->w->active);
  678. if (fs->wp == NULL)
  679. return (-1);
  680. return (0);
  681. }
  682. /* Try as an offset. */
  683. if (pane[0] == '+' || pane[0] == '-') {
  684. if (pane[1] != '\0')
  685. n = strtonum(pane + 1, 1, INT_MAX, NULL);
  686. else
  687. n = 1;
  688. wp = fs->w->active;
  689. if (pane[0] == '+')
  690. fs->wp = window_pane_next_by_number(fs->w, wp, n);
  691. else
  692. fs->wp = window_pane_previous_by_number(fs->w, wp, n);
  693. if (fs->wp != NULL)
  694. return (0);
  695. }
  696. /* Get pane by index. */
  697. idx = strtonum(pane, 0, INT_MAX, &errstr);
  698. if (errstr == NULL) {
  699. fs->wp = window_pane_at_index(fs->w, idx);
  700. if (fs->wp != NULL)
  701. return (0);
  702. }
  703. /* Try as a description. */
  704. fs->wp = window_find_string(fs->w, pane);
  705. if (fs->wp != NULL)
  706. return (0);
  707. return (-1);
  708. }
  709. /* Clear state. */
  710. void
  711. cmd_find_clear_state(struct cmd_find_state *fs, struct cmd_q *cmdq, int flags)
  712. {
  713. memset(fs, 0, sizeof *fs);
  714. fs->cmdq = cmdq;
  715. fs->flags = flags;
  716. fs->idx = -1;
  717. }
  718. /* Check if a state if valid. */
  719. int
  720. cmd_find_valid_state(struct cmd_find_state *fs)
  721. {
  722. struct winlink *wl;
  723. if (fs->s == NULL || fs->wl == NULL || fs->w == NULL || fs->wp == NULL)
  724. return (0);
  725. if (!session_alive(fs->s))
  726. return (0);
  727. RB_FOREACH(wl, winlinks, &fs->s->windows) {
  728. if (wl->window == fs->w && wl == fs->wl)
  729. break;
  730. }
  731. if (wl == NULL)
  732. return (0);
  733. if (fs->w != fs->wl->window)
  734. return (0);
  735. if (!window_has_pane(fs->w, fs->wp))
  736. return (0);
  737. return (window_pane_visible(fs->wp));
  738. }
  739. /* Copy a state. */
  740. void
  741. cmd_find_copy_state(struct cmd_find_state *dst, struct cmd_find_state *src)
  742. {
  743. dst->s = src->s;
  744. dst->wl = src->wl;
  745. dst->idx = src->idx;
  746. dst->w = src->w;
  747. dst->wp = src->wp;
  748. }
  749. /* Log the result. */
  750. void
  751. cmd_find_log_state(const char *prefix, struct cmd_find_state *fs)
  752. {
  753. if (fs->s != NULL)
  754. log_debug("%s: s=$%u", prefix, fs->s->id);
  755. else
  756. log_debug("%s: s=none", prefix);
  757. if (fs->wl != NULL) {
  758. log_debug("%s: wl=%u %d w=@%u %s", prefix, fs->wl->idx,
  759. fs->wl->window == fs->w, fs->w->id, fs->w->name);
  760. } else
  761. log_debug("%s: wl=none", prefix);
  762. if (fs->wp != NULL)
  763. log_debug("%s: wp=%%%u", prefix, fs->wp->id);
  764. else
  765. log_debug("%s: wp=none", prefix);
  766. if (fs->idx != -1)
  767. log_debug("%s: idx=%d", prefix, fs->idx);
  768. else
  769. log_debug("%s: idx=none", prefix);
  770. }
  771. /* Find state from a session. */
  772. int
  773. cmd_find_from_session(struct cmd_find_state *fs, struct session *s)
  774. {
  775. cmd_find_clear_state(fs, NULL, 0);
  776. fs->s = s;
  777. fs->wl = fs->s->curw;
  778. fs->w = fs->wl->window;
  779. fs->wp = fs->w->active;
  780. cmd_find_log_state(__func__, fs);
  781. return (0);
  782. }
  783. /* Find state from a winlink. */
  784. int
  785. cmd_find_from_winlink(struct cmd_find_state *fs, struct session *s,
  786. struct winlink *wl)
  787. {
  788. cmd_find_clear_state(fs, NULL, 0);
  789. fs->s = s;
  790. fs->wl = wl;
  791. fs->w = wl->window;
  792. fs->wp = wl->window->active;
  793. cmd_find_log_state(__func__, fs);
  794. return (0);
  795. }
  796. /* Find state from a window. */
  797. int
  798. cmd_find_from_window(struct cmd_find_state *fs, struct window *w)
  799. {
  800. cmd_find_clear_state(fs, NULL, 0);
  801. fs->w = w;
  802. if (cmd_find_best_session_with_window(fs) != 0)
  803. return (-1);
  804. if (cmd_find_best_winlink_with_window(fs) != 0)
  805. return (-1);
  806. cmd_find_log_state(__func__, fs);
  807. return (0);
  808. }
  809. /* Find state from a pane. */
  810. int
  811. cmd_find_from_pane(struct cmd_find_state *fs, struct window_pane *wp)
  812. {
  813. if (cmd_find_from_window(fs, wp->window) != 0)
  814. return (-1);
  815. fs->wp = wp;
  816. cmd_find_log_state(__func__, fs);
  817. return (0);
  818. }
  819. /* Find current state. */
  820. int
  821. cmd_find_current(struct cmd_find_state *fs, struct cmd_q *cmdq, int flags)
  822. {
  823. cmd_find_clear_state(fs, cmdq, flags);
  824. if (cmd_find_current_session(fs) != 0) {
  825. if (~flags & CMD_FIND_QUIET)
  826. cmdq_error(cmdq, "no current session");
  827. return (-1);
  828. }
  829. return (0);
  830. }
  831. /*
  832. * Split target into pieces and resolve for the given type. Fills in the given
  833. * state. Returns 0 on success or -1 on error.
  834. */
  835. int
  836. cmd_find_target(struct cmd_find_state *fs, struct cmd_find_state *current,
  837. struct cmd_q *cmdq, const char *target, enum cmd_find_type type, int flags)
  838. {
  839. struct mouse_event *m;
  840. char *colon, *period, *copy = NULL;
  841. const char *session, *window, *pane;
  842. /* Log the arguments. */
  843. if (target == NULL)
  844. log_debug("%s: target none, type %d", __func__, type);
  845. else
  846. log_debug("%s: target %s, type %d", __func__, target, type);
  847. log_debug("%s: cmdq %p, flags %#x", __func__, cmdq, flags);
  848. /* Clear new state. */
  849. cmd_find_clear_state(fs, cmdq, flags);
  850. /* Find current state. */
  851. if (server_check_marked() && (flags & CMD_FIND_DEFAULT_MARKED))
  852. fs->current = &marked_pane;
  853. else if (cmd_find_valid_state(&cmdq->current))
  854. fs->current = &cmdq->current;
  855. else
  856. fs->current = current;
  857. /* An empty or NULL target is the current. */
  858. if (target == NULL || *target == '\0')
  859. goto current;
  860. /* Mouse target is a plain = or {mouse}. */
  861. if (strcmp(target, "=") == 0 || strcmp(target, "{mouse}") == 0) {
  862. m = &cmdq->item->mouse;
  863. switch (type) {
  864. case CMD_FIND_PANE:
  865. fs->wp = cmd_mouse_pane(m, &fs->s, &fs->wl);
  866. if (fs->wp != NULL)
  867. fs->w = fs->wl->window;
  868. break;
  869. case CMD_FIND_WINDOW:
  870. case CMD_FIND_SESSION:
  871. fs->wl = cmd_mouse_window(m, &fs->s);
  872. if (fs->wl != NULL) {
  873. fs->w = fs->wl->window;
  874. fs->wp = fs->w->active;
  875. }
  876. break;
  877. }
  878. if (fs->wp == NULL) {
  879. if (~flags & CMD_FIND_QUIET)
  880. cmdq_error(cmdq, "no mouse target");
  881. goto error;
  882. }
  883. goto found;
  884. }
  885. /* Marked target is a plain ~ or {marked}. */
  886. if (strcmp(target, "~") == 0 || strcmp(target, "{marked}") == 0) {
  887. if (!server_check_marked()) {
  888. if (~flags & CMD_FIND_QUIET)
  889. cmdq_error(cmdq, "no marked target");
  890. goto error;
  891. }
  892. cmd_find_copy_state(fs, &marked_pane);
  893. goto found;
  894. }
  895. /* Find separators if they exist. */
  896. copy = xstrdup(target);
  897. colon = strchr(copy, ':');
  898. if (colon != NULL)
  899. *colon++ = '\0';
  900. if (colon == NULL)
  901. period = strchr(copy, '.');
  902. else
  903. period = strchr(colon, '.');
  904. if (period != NULL)
  905. *period++ = '\0';
  906. /* Set session, window and pane parts. */
  907. session = window = pane = NULL;
  908. if (colon != NULL && period != NULL) {
  909. session = copy;
  910. window = colon;
  911. pane = period;
  912. } else if (colon != NULL && period == NULL) {
  913. session = copy;
  914. window = colon;
  915. } else if (colon == NULL && period != NULL) {
  916. window = copy;
  917. pane = period;
  918. } else {
  919. if (*copy == '$')
  920. session = copy;
  921. else if (*copy == '@')
  922. window = copy;
  923. else if (*copy == '%')
  924. pane = copy;
  925. else {
  926. switch (type) {
  927. case CMD_FIND_SESSION:
  928. session = copy;
  929. break;
  930. case CMD_FIND_WINDOW:
  931. window = copy;
  932. break;
  933. case CMD_FIND_PANE:
  934. pane = copy;
  935. break;
  936. }
  937. }
  938. }
  939. /* Set exact match flags. */
  940. if (session != NULL && *session == '=') {
  941. session++;
  942. fs->flags |= CMD_FIND_EXACT_SESSION;
  943. }
  944. if (window != NULL && *window == '=') {
  945. window++;
  946. fs->flags |= CMD_FIND_EXACT_WINDOW;
  947. }
  948. /* Empty is the same as NULL. */
  949. if (session != NULL && *session == '\0')
  950. session = NULL;
  951. if (window != NULL && *window == '\0')
  952. window = NULL;
  953. if (pane != NULL && *pane == '\0')
  954. pane = NULL;
  955. /* Map though conversion table. */
  956. if (session != NULL)
  957. session = cmd_find_map_table(cmd_find_session_table, session);
  958. if (window != NULL)
  959. window = cmd_find_map_table(cmd_find_window_table, window);
  960. if (pane != NULL)
  961. pane = cmd_find_map_table(cmd_find_pane_table, pane);
  962. log_debug("target %s (flags %#x): session=%s, window=%s, pane=%s",
  963. target, flags, session == NULL ? "none" : session,
  964. window == NULL ? "none" : window, pane == NULL ? "none" : pane);
  965. /* No pane is allowed if want an index. */
  966. if (pane != NULL && (flags & CMD_FIND_WINDOW_INDEX)) {
  967. if (~flags & CMD_FIND_QUIET)
  968. cmdq_error(cmdq, "can't specify pane here");
  969. goto error;
  970. }
  971. /* If the session isn't NULL, look it up. */
  972. if (session != NULL) {
  973. /* This will fill in session. */
  974. if (cmd_find_get_session(fs, session) != 0)
  975. goto no_session;
  976. /* If window and pane are NULL, use that session's current. */
  977. if (window == NULL && pane == NULL) {
  978. fs->wl = fs->s->curw;
  979. fs->idx = -1;
  980. fs->w = fs->wl->window;
  981. fs->wp = fs->w->active;
  982. goto found;
  983. }
  984. /* If window is present but pane not, find window in session. */
  985. if (window != NULL && pane == NULL) {
  986. /* This will fill in winlink and window. */
  987. if (cmd_find_get_window_with_session(fs, window) != 0)
  988. goto no_window;
  989. fs->wp = fs->wl->window->active;
  990. goto found;
  991. }
  992. /* If pane is present but window not, find pane. */
  993. if (window == NULL && pane != NULL) {
  994. /* This will fill in winlink and window and pane. */
  995. if (cmd_find_get_pane_with_session(fs, pane) != 0)
  996. goto no_pane;
  997. goto found;
  998. }
  999. /*
  1000. * If window and pane are present, find both in session. This
  1001. * will fill in winlink and window.
  1002. */
  1003. if (cmd_find_get_window_with_session(fs, window) != 0)
  1004. goto no_window;
  1005. /* This will fill in pane. */
  1006. if (cmd_find_get_pane_with_window(fs, pane) != 0)
  1007. goto no_pane;
  1008. goto found;
  1009. }
  1010. /* No session. If window and pane, try them. */
  1011. if (window != NULL && pane != NULL) {
  1012. /* This will fill in session, winlink and window. */
  1013. if (cmd_find_get_window(fs, window) != 0)
  1014. goto no_window;
  1015. /* This will fill in pane. */
  1016. if (cmd_find_get_pane_with_window(fs, pane) != 0)
  1017. goto no_pane;
  1018. goto found;
  1019. }
  1020. /* If just window is present, try it. */
  1021. if (window != NULL && pane == NULL) {
  1022. /* This will fill in session, winlink and window. */
  1023. if (cmd_find_get_window(fs, window) != 0)
  1024. goto no_window;
  1025. fs->wp = fs->wl->window->active;
  1026. goto found;
  1027. }
  1028. /* If just pane is present, try it. */
  1029. if (window == NULL && pane != NULL) {
  1030. /* This will fill in session, winlink, window and pane. */
  1031. if (cmd_find_get_pane(fs, pane) != 0)
  1032. goto no_pane;
  1033. goto found;
  1034. }
  1035. current:
  1036. /* Use the current session. */
  1037. cmd_find_copy_state(fs, fs->current);
  1038. if (flags & CMD_FIND_WINDOW_INDEX)
  1039. fs->idx = -1;
  1040. goto found;
  1041. error:
  1042. fs->current = NULL;
  1043. log_debug(" error");
  1044. free(copy);
  1045. return (-1);
  1046. found:
  1047. fs->current = NULL;
  1048. cmd_find_log_state(__func__, fs);
  1049. free(copy);
  1050. return (0);
  1051. no_session:
  1052. if (~flags & CMD_FIND_QUIET)
  1053. cmdq_error(cmdq, "can't find session %s", session);
  1054. goto error;
  1055. no_window:
  1056. if (~flags & CMD_FIND_QUIET)
  1057. cmdq_error(cmdq, "can't find window %s", window);
  1058. goto error;
  1059. no_pane:
  1060. if (~flags & CMD_FIND_QUIET)
  1061. cmdq_error(cmdq, "can't find pane %s", pane);
  1062. goto error;
  1063. }
  1064. /* Find the target client or report an error and return NULL. */
  1065. struct client *
  1066. cmd_find_client(struct cmd_q *cmdq, const char *target, int quiet)
  1067. {
  1068. struct client *c;
  1069. char *copy;
  1070. size_t size;
  1071. const char *path;
  1072. /* A NULL argument means the current client. */
  1073. if (cmdq != NULL && target == NULL) {
  1074. c = cmd_find_current_client(cmdq);
  1075. if (c == NULL && !quiet)
  1076. cmdq_error(cmdq, "no current client");
  1077. log_debug("%s: no target, return %p", __func__, c);
  1078. return (c);
  1079. }
  1080. copy = xstrdup(target);
  1081. /* Trim a single trailing colon if any. */
  1082. size = strlen(copy);
  1083. if (size != 0 && copy[size - 1] == ':')
  1084. copy[size - 1] = '\0';
  1085. /* Check path of each client. */
  1086. TAILQ_FOREACH(c, &clients, entry) {
  1087. if (c->session == NULL || c->tty.path == NULL)
  1088. continue;
  1089. path = c->tty.path;
  1090. /* Try for exact match. */
  1091. if (strcmp(copy, path) == 0)
  1092. break;
  1093. /* Try without leading /dev. */
  1094. if (strncmp(path, _PATH_DEV, (sizeof _PATH_DEV) - 1) != 0)
  1095. continue;
  1096. if (strcmp(copy, path + (sizeof _PATH_DEV) - 1) == 0)
  1097. break;
  1098. }
  1099. /* If no client found, report an error. */
  1100. if (c == NULL && !quiet)
  1101. cmdq_error(cmdq, "can't find client %s", copy);
  1102. free(copy);
  1103. log_debug("%s: target %s, return %p", __func__, target, c);
  1104. return (c);
  1105. }