GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/tmux/cmd-find.c Lines: 0 614 0.0 %
Date: 2016-12-06 Branches: 0 485 0.0 %

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