xref: /openbsd-src/usr.bin/tmux/format.c (revision 03adc85b7600a1f8f04886b8321c1c1c0c4933d4)
1 /* $OpenBSD: format.c,v 1.117 2017/01/16 14:49:14 nicm Exp $ */
2 
3 /*
4  * Copyright (c) 2011 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 #include <sys/wait.h>
21 
22 #include <ctype.h>
23 #include <errno.h>
24 #include <libgen.h>
25 #include <netdb.h>
26 #include <stdarg.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <time.h>
30 #include <unistd.h>
31 
32 #include "tmux.h"
33 
34 /*
35  * Build a list of key-value pairs and use them to expand #{key} entries in a
36  * string.
37  */
38 
39 struct format_entry;
40 typedef void (*format_cb)(struct format_tree *, struct format_entry *);
41 
42 static void	 format_job_callback(struct job *);
43 static char	*format_job_get(struct format_tree *, const char *);
44 static void	 format_job_timer(int, short, void *);
45 
46 static void	 format_cb_host(struct format_tree *, struct format_entry *);
47 static void	 format_cb_host_short(struct format_tree *,
48 		     struct format_entry *);
49 static void	 format_cb_pid(struct format_tree *, struct format_entry *);
50 static void	 format_cb_session_alerts(struct format_tree *,
51 		     struct format_entry *);
52 static void	 format_cb_window_layout(struct format_tree *,
53 		     struct format_entry *);
54 static void	 format_cb_window_visible_layout(struct format_tree *,
55 		     struct format_entry *);
56 static void	 format_cb_start_command(struct format_tree *,
57 		     struct format_entry *);
58 static void	 format_cb_current_command(struct format_tree *,
59 		     struct format_entry *);
60 static void	 format_cb_history_bytes(struct format_tree *,
61 		     struct format_entry *);
62 static void	 format_cb_pane_tabs(struct format_tree *,
63 		     struct format_entry *);
64 
65 static char	*format_find(struct format_tree *, const char *, int);
66 static void	 format_add_cb(struct format_tree *, const char *, format_cb);
67 static void	 format_add_tv(struct format_tree *, const char *,
68 		     struct timeval *);
69 static int	 format_replace(struct format_tree *, const char *, size_t,
70 		     char **, size_t *, size_t *);
71 
72 static void	 format_defaults_session(struct format_tree *,
73 		     struct session *);
74 static void	 format_defaults_client(struct format_tree *, struct client *);
75 static void	 format_defaults_winlink(struct format_tree *, struct session *,
76 		     struct winlink *);
77 
78 /* Entry in format job tree. */
79 struct format_job {
80 	const char		*cmd;
81 	const char		*expanded;
82 
83 	time_t			 last;
84 	char			*out;
85 
86 	struct job		*job;
87 	int			 status;
88 
89 	RB_ENTRY(format_job)	 entry;
90 };
91 
92 /* Format job tree. */
93 static struct event format_job_event;
94 static int format_job_cmp(struct format_job *, struct format_job *);
95 static RB_HEAD(format_job_tree, format_job) format_jobs = RB_INITIALIZER();
96 RB_GENERATE_STATIC(format_job_tree, format_job, entry, format_job_cmp);
97 
98 /* Format job tree comparison function. */
99 static int
100 format_job_cmp(struct format_job *fj1, struct format_job *fj2)
101 {
102 	return (strcmp(fj1->cmd, fj2->cmd));
103 }
104 
105 /* Format modifiers. */
106 #define FORMAT_TIMESTRING 0x1
107 #define FORMAT_BASENAME 0x2
108 #define FORMAT_DIRNAME 0x4
109 #define FORMAT_SUBSTITUTE 0x8
110 
111 /* Entry in format tree. */
112 struct format_entry {
113 	char			*key;
114 	char			*value;
115 	time_t			 t;
116 	format_cb		 cb;
117 	RB_ENTRY(format_entry)	 entry;
118 };
119 
120 /* Format entry tree. */
121 struct format_tree {
122 	struct window		*w;
123 	struct session		*s;
124 	struct window_pane	*wp;
125 
126 	int			 flags;
127 
128 	RB_HEAD(format_entry_tree, format_entry) tree;
129 };
130 static int format_entry_cmp(struct format_entry *, struct format_entry *);
131 RB_GENERATE_STATIC(format_entry_tree, format_entry, entry, format_entry_cmp);
132 
133 /* Format entry tree comparison function. */
134 static int
135 format_entry_cmp(struct format_entry *fe1, struct format_entry *fe2)
136 {
137 	return (strcmp(fe1->key, fe2->key));
138 }
139 
140 /* Single-character uppercase aliases. */
141 static const char *format_upper[] = {
142 	NULL,		/* A */
143 	NULL,		/* B */
144 	NULL,		/* C */
145 	"pane_id",	/* D */
146 	NULL,		/* E */
147 	"window_flags",	/* F */
148 	NULL,		/* G */
149 	"host",		/* H */
150 	"window_index",	/* I */
151 	NULL,		/* J */
152 	NULL,		/* K */
153 	NULL,		/* L */
154 	NULL,		/* M */
155 	NULL,		/* N */
156 	NULL,		/* O */
157 	"pane_index",	/* P */
158 	NULL,		/* Q */
159 	NULL,		/* R */
160 	"session_name",	/* S */
161 	"pane_title",	/* T */
162 	NULL,		/* U */
163 	NULL,		/* V */
164 	"window_name",	/* W */
165 	NULL,		/* X */
166 	NULL,		/* Y */
167 	NULL 		/* Z */
168 };
169 
170 /* Single-character lowercase aliases. */
171 static const char *format_lower[] = {
172 	NULL,		/* a */
173 	NULL,		/* b */
174 	NULL,		/* c */
175 	NULL,		/* d */
176 	NULL,		/* e */
177 	NULL,		/* f */
178 	NULL,		/* g */
179 	"host_short",	/* h */
180 	NULL,		/* i */
181 	NULL,		/* j */
182 	NULL,		/* k */
183 	NULL,		/* l */
184 	NULL,		/* m */
185 	NULL,		/* n */
186 	NULL,		/* o */
187 	NULL,		/* p */
188 	NULL,		/* q */
189 	NULL,		/* r */
190 	NULL,		/* s */
191 	NULL,		/* t */
192 	NULL,		/* u */
193 	NULL,		/* v */
194 	NULL,		/* w */
195 	NULL,		/* x */
196 	NULL,		/* y */
197 	NULL		/* z */
198 };
199 
200 /* Format job callback. */
201 static void
202 format_job_callback(struct job *job)
203 {
204 	struct format_job	*fj = job->data;
205 	char			*line, *buf;
206 	size_t			 len;
207 	struct client		*c;
208 
209 	fj->job = NULL;
210 	free(fj->out);
211 
212 	buf = NULL;
213 	if ((line = evbuffer_readline(job->event->input)) == NULL) {
214 		len = EVBUFFER_LENGTH(job->event->input);
215 		buf = xmalloc(len + 1);
216 		if (len != 0)
217 			memcpy(buf, EVBUFFER_DATA(job->event->input), len);
218 		buf[len] = '\0';
219 	} else
220 		buf = line;
221 	fj->out = buf;
222 
223 	if (fj->status) {
224 		TAILQ_FOREACH(c, &clients, entry)
225 		    server_status_client(c);
226 		fj->status = 0;
227 	}
228 
229 	log_debug("%s: %s: %s", __func__, fj->cmd, fj->out);
230 }
231 
232 /* Find a job. */
233 static char *
234 format_job_get(struct format_tree *ft, const char *cmd)
235 {
236 	struct format_job	 fj0, *fj;
237 	time_t			 t;
238 	char			*expanded;
239 	int			 force;
240 
241 	fj0.cmd = cmd;
242 	if ((fj = RB_FIND(format_job_tree, &format_jobs, &fj0)) == NULL) {
243 		fj = xcalloc(1, sizeof *fj);
244 		fj->cmd = xstrdup(cmd);
245 		fj->expanded = NULL;
246 
247 		xasprintf(&fj->out, "<'%s' not ready>", fj->cmd);
248 
249 		RB_INSERT(format_job_tree, &format_jobs, fj);
250 	}
251 
252 	expanded = format_expand(ft, cmd);
253 	if (fj->expanded == NULL || strcmp(expanded, fj->expanded) != 0) {
254 		free((void *)fj->expanded);
255 		fj->expanded = xstrdup(expanded);
256 		force = 1;
257 	} else
258 		force = (ft->flags & FORMAT_FORCE);
259 
260 	t = time(NULL);
261 	if (fj->job == NULL && (force || fj->last != t)) {
262 		fj->job = job_run(expanded, NULL, NULL, format_job_callback,
263 		    NULL, fj);
264 		if (fj->job == NULL) {
265 			free(fj->out);
266 			xasprintf(&fj->out, "<'%s' didn't start>", fj->cmd);
267 		}
268 		fj->last = t;
269 	}
270 
271 	if (ft->flags & FORMAT_STATUS)
272 		fj->status = 1;
273 
274 	free(expanded);
275 	return (format_expand(ft, fj->out));
276 }
277 
278 /* Remove old jobs. */
279 static void
280 format_job_timer(__unused int fd, __unused short events, __unused void *arg)
281 {
282 	struct format_job	*fj, *fj1;
283 	time_t			 now;
284 	struct timeval		 tv = { .tv_sec = 60 };
285 
286 	now = time(NULL);
287 	RB_FOREACH_SAFE(fj, format_job_tree, &format_jobs, fj1) {
288 		if (fj->last > now || now - fj->last < 3600)
289 			continue;
290 		RB_REMOVE(format_job_tree, &format_jobs, fj);
291 
292 		log_debug("%s: %s", __func__, fj->cmd);
293 
294 		if (fj->job != NULL)
295 			job_free(fj->job);
296 
297 		free((void *)fj->expanded);
298 		free((void *)fj->cmd);
299 		free(fj->out);
300 
301 		free(fj);
302 	}
303 
304 	evtimer_del(&format_job_event);
305 	evtimer_add(&format_job_event, &tv);
306 }
307 
308 /* Callback for host. */
309 static void
310 format_cb_host(__unused struct format_tree *ft, struct format_entry *fe)
311 {
312 	char host[HOST_NAME_MAX + 1];
313 
314 	if (gethostname(host, sizeof host) != 0)
315 		fe->value = xstrdup("");
316 	else
317 		fe->value = xstrdup(host);
318 }
319 
320 /* Callback for host_short. */
321 static void
322 format_cb_host_short(__unused struct format_tree *ft, struct format_entry *fe)
323 {
324 	char host[HOST_NAME_MAX + 1], *cp;
325 
326 	if (gethostname(host, sizeof host) != 0)
327 		fe->value = xstrdup("");
328 	else {
329 		if ((cp = strchr(host, '.')) != NULL)
330 			*cp = '\0';
331 		fe->value = xstrdup(host);
332 	}
333 }
334 
335 /* Callback for pid. */
336 static void
337 format_cb_pid(__unused struct format_tree *ft, struct format_entry *fe)
338 {
339 	xasprintf(&fe->value, "%ld", (long)getpid());
340 }
341 
342 /* Callback for session_alerts. */
343 static void
344 format_cb_session_alerts(struct format_tree *ft, struct format_entry *fe)
345 {
346 	struct session	*s = ft->s;
347 	struct winlink	*wl;
348 	char		 alerts[256], tmp[16];
349 
350 	if (s == NULL)
351 		return;
352 
353 	*alerts = '\0';
354 	RB_FOREACH(wl, winlinks, &s->windows) {
355 		if ((wl->flags & WINLINK_ALERTFLAGS) == 0)
356 			continue;
357 		xsnprintf(tmp, sizeof tmp, "%u", wl->idx);
358 
359 		if (*alerts != '\0')
360 			strlcat(alerts, ",", sizeof alerts);
361 		strlcat(alerts, tmp, sizeof alerts);
362 		if (wl->flags & WINLINK_ACTIVITY)
363 			strlcat(alerts, "#", sizeof alerts);
364 		if (wl->flags & WINLINK_BELL)
365 			strlcat(alerts, "!", sizeof alerts);
366 		if (wl->flags & WINLINK_SILENCE)
367 			strlcat(alerts, "~", sizeof alerts);
368 	}
369 	fe->value = xstrdup(alerts);
370 }
371 
372 /* Callback for window_layout. */
373 static void
374 format_cb_window_layout(struct format_tree *ft, struct format_entry *fe)
375 {
376 	struct window	*w = ft->w;
377 
378 	if (w == NULL)
379 		return;
380 
381 	if (w->saved_layout_root != NULL)
382 		fe->value = layout_dump(w->saved_layout_root);
383 	else
384 		fe->value = layout_dump(w->layout_root);
385 }
386 
387 /* Callback for window_visible_layout. */
388 static void
389 format_cb_window_visible_layout(struct format_tree *ft, struct format_entry *fe)
390 {
391 	struct window	*w = ft->w;
392 
393 	if (w == NULL)
394 		return;
395 
396 	fe->value = layout_dump(w->layout_root);
397 }
398 
399 /* Callback for pane_start_command. */
400 static void
401 format_cb_start_command(struct format_tree *ft, struct format_entry *fe)
402 {
403 	struct window_pane	*wp = ft->wp;
404 
405 	if (wp == NULL)
406 		return;
407 
408 	fe->value = cmd_stringify_argv(wp->argc, wp->argv);
409 }
410 
411 /* Callback for pane_current_command. */
412 static void
413 format_cb_current_command(struct format_tree *ft, struct format_entry *fe)
414 {
415 	struct window_pane	*wp = ft->wp;
416 	char			*cmd;
417 
418 	if (wp == NULL)
419 		return;
420 
421 	cmd = get_proc_name(wp->fd, wp->tty);
422 	if (cmd == NULL || *cmd == '\0') {
423 		free(cmd);
424 		cmd = cmd_stringify_argv(wp->argc, wp->argv);
425 		if (cmd == NULL || *cmd == '\0') {
426 			free(cmd);
427 			cmd = xstrdup(wp->shell);
428 		}
429 	}
430 	fe->value = parse_window_name(cmd);
431 	free(cmd);
432 }
433 
434 /* Callback for history_bytes. */
435 static void
436 format_cb_history_bytes(struct format_tree *ft, struct format_entry *fe)
437 {
438 	struct window_pane	*wp = ft->wp;
439 	struct grid		*gd;
440 	struct grid_line	*gl;
441 	unsigned long long	 size;
442 	u_int			 i;
443 
444 	if (wp == NULL)
445 		return;
446 	gd = wp->base.grid;
447 
448 	size = 0;
449 	for (i = 0; i < gd->hsize; i++) {
450 		gl = &gd->linedata[i];
451 		size += gl->cellsize * sizeof *gl->celldata;
452 		size += gl->extdsize * sizeof *gl->extddata;
453 	}
454 	size += gd->hsize * sizeof *gd->linedata;
455 
456 	xasprintf(&fe->value, "%llu", size);
457 }
458 
459 /* Callback for pane_tabs. */
460 static void
461 format_cb_pane_tabs(struct format_tree *ft, struct format_entry *fe)
462 {
463 	struct window_pane	*wp = ft->wp;
464 	struct evbuffer		*buffer;
465 	u_int			 i;
466 	int			 size;
467 
468 	if (wp == NULL)
469 		return;
470 
471 	buffer = evbuffer_new();
472 	for (i = 0; i < wp->base.grid->sx; i++) {
473 		if (!bit_test(wp->base.tabs, i))
474 			continue;
475 
476 		if (EVBUFFER_LENGTH(buffer) > 0)
477 			evbuffer_add(buffer, ",", 1);
478 		evbuffer_add_printf(buffer, "%u", i);
479 	}
480 	size = EVBUFFER_LENGTH(buffer);
481 	xasprintf(&fe->value, "%.*s", size, EVBUFFER_DATA(buffer));
482 	evbuffer_free(buffer);
483 }
484 
485 /* Merge a format tree. */
486 static void
487 format_merge(struct format_tree *ft, struct format_tree *from)
488 {
489 	struct format_entry	*fe;
490 
491 	RB_FOREACH(fe, format_entry_tree, &from->tree) {
492 		if (fe->value != NULL)
493 			format_add(ft, fe->key, "%s", fe->value);
494 	}
495 
496 }
497 
498 /* Create a new tree. */
499 struct format_tree *
500 format_create(struct cmdq_item *item, int flags)
501 {
502 	struct format_tree	*ft;
503 
504 	if (!event_initialized(&format_job_event)) {
505 		evtimer_set(&format_job_event, format_job_timer, NULL);
506 		format_job_timer(-1, 0, NULL);
507 	}
508 
509 	ft = xcalloc(1, sizeof *ft);
510 	RB_INIT(&ft->tree);
511 	ft->flags = flags;
512 
513 	format_add_cb(ft, "host", format_cb_host);
514 	format_add_cb(ft, "host_short", format_cb_host_short);
515 	format_add_cb(ft, "pid", format_cb_pid);
516 	format_add(ft, "socket_path", "%s", socket_path);
517 	format_add_tv(ft, "start_time", &start_time);
518 
519 	if (item != NULL && item->cmd != NULL)
520 		format_add(ft, "command", "%s", item->cmd->entry->name);
521 	if (item != NULL && item->formats != NULL)
522 		format_merge(ft, item->formats);
523 
524 	return (ft);
525 }
526 
527 /* Free a tree. */
528 void
529 format_free(struct format_tree *ft)
530 {
531 	struct format_entry	*fe, *fe1;
532 
533 	RB_FOREACH_SAFE(fe, format_entry_tree, &ft->tree, fe1) {
534 		RB_REMOVE(format_entry_tree, &ft->tree, fe);
535 		free(fe->value);
536 		free(fe->key);
537 		free(fe);
538 	}
539 
540 	free(ft);
541 }
542 
543 /* Add a key-value pair. */
544 void
545 format_add(struct format_tree *ft, const char *key, const char *fmt, ...)
546 {
547 	struct format_entry	*fe;
548 	struct format_entry	*fe_now;
549 	va_list			 ap;
550 
551 	fe = xmalloc(sizeof *fe);
552 	fe->key = xstrdup(key);
553 
554 	fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe);
555 	if (fe_now != NULL) {
556 		free(fe->key);
557 		free(fe);
558 		free(fe_now->value);
559 		fe = fe_now;
560 	}
561 
562 	fe->cb = NULL;
563 	fe->t = 0;
564 
565 	va_start(ap, fmt);
566 	xvasprintf(&fe->value, fmt, ap);
567 	va_end(ap);
568 }
569 
570 /* Add a key and time. */
571 static void
572 format_add_tv(struct format_tree *ft, const char *key, struct timeval *tv)
573 {
574 	struct format_entry	*fe;
575 	struct format_entry	*fe_now;
576 
577 	fe = xmalloc(sizeof *fe);
578 	fe->key = xstrdup(key);
579 
580 	fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe);
581 	if (fe_now != NULL) {
582 		free(fe->key);
583 		free(fe);
584 		free(fe_now->value);
585 		fe = fe_now;
586 	}
587 
588 	fe->cb = NULL;
589 	fe->t = tv->tv_sec;
590 
591 	fe->value = NULL;
592 }
593 
594 /* Add a key and function. */
595 static void
596 format_add_cb(struct format_tree *ft, const char *key, format_cb cb)
597 {
598 	struct format_entry	*fe;
599 	struct format_entry	*fe_now;
600 
601 	fe = xmalloc(sizeof *fe);
602 	fe->key = xstrdup(key);
603 
604 	fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe);
605 	if (fe_now != NULL) {
606 		free(fe->key);
607 		free(fe);
608 		free(fe_now->value);
609 		fe = fe_now;
610 	}
611 
612 	fe->cb = cb;
613 	fe->t = 0;
614 
615 	fe->value = NULL;
616 }
617 
618 /* Find a format entry. */
619 static char *
620 format_find(struct format_tree *ft, const char *key, int modifiers)
621 {
622 	struct format_entry	*fe, fe_find;
623 	struct environ_entry	*envent;
624 	static char		 s[64];
625 	struct options_entry	*o;
626 	const char		*found;
627 	int			 idx;
628 	char			*copy, *saved;
629 
630 	if (~modifiers & FORMAT_TIMESTRING) {
631 		o = options_parse_get(global_options, key, &idx, 0);
632 		if (o == NULL && ft->w != NULL)
633 			o = options_parse_get(ft->w->options, key, &idx, 0);
634 		if (o == NULL)
635 			o = options_parse_get(global_w_options, key, &idx, 0);
636 		if (o == NULL && ft->s != NULL)
637 			o = options_parse_get(ft->s->options, key, &idx, 0);
638 		if (o == NULL)
639 			o = options_parse_get(global_s_options, key, &idx, 0);
640 		if (o != NULL) {
641 			found = options_tostring(o, idx);
642 			goto found;
643 		}
644 	}
645 	found = NULL;
646 
647 	fe_find.key = (char *) key;
648 	fe = RB_FIND(format_entry_tree, &ft->tree, &fe_find);
649 	if (fe != NULL) {
650 		if (modifiers & FORMAT_TIMESTRING) {
651 			if (fe->t == 0)
652 				return (NULL);
653 			ctime_r(&fe->t, s);
654 			s[strcspn(s, "\n")] = '\0';
655 			found = s;
656 			goto found;
657 		}
658 		if (fe->t != 0) {
659 			xsnprintf(s, sizeof s, "%lld", (long long)fe->t);
660 			found = s;
661 			goto found;
662 		}
663 		if (fe->value == NULL && fe->cb != NULL)
664 			fe->cb(ft, fe);
665 		found = fe->value;
666 		goto found;
667 	}
668 
669 	if (~modifiers & FORMAT_TIMESTRING) {
670 		envent = NULL;
671 		if (ft->s != NULL)
672 			envent = environ_find(ft->s->environ, key);
673 		if (envent == NULL)
674 			envent = environ_find(global_environ, key);
675 		if (envent != NULL) {
676 			found = envent->value;
677 			goto found;
678 		}
679 	}
680 
681 	return (NULL);
682 
683 found:
684 	if (found == NULL)
685 		return (NULL);
686 	copy = xstrdup(found);
687 	if (modifiers & FORMAT_BASENAME) {
688 		saved = copy;
689 		copy = xstrdup(basename(saved));
690 		free(saved);
691 	}
692 	if (modifiers & FORMAT_DIRNAME) {
693 		saved = copy;
694 		copy = xstrdup(dirname(saved));
695 		free(saved);
696 	}
697 	return (copy);
698 }
699 
700 /* Skip until comma. */
701 static char *
702 format_skip(char *s)
703 {
704 	int	brackets = 0;
705 
706 	for (; *s != '\0'; s++) {
707 		if (*s == '{')
708 			brackets++;
709 		if (*s == '}')
710 			brackets--;
711 		if (*s == ',' && brackets == 0)
712 			break;
713 	}
714 	if (*s == '\0')
715 		return (NULL);
716 	return (s);
717 }
718 
719 /* Return left and right alternatives separated by commas. */
720 static int
721 format_choose(char *s, char **left, char **right)
722 {
723 	char	*cp;
724 
725 	cp = format_skip(s);
726 	if (cp == NULL)
727 		return (-1);
728 	*cp = '\0';
729 
730 	*left = s;
731 	*right = cp + 1;
732 	return (0);
733 }
734 
735 /* Is this true? */
736 static int
737 format_true(const char *s)
738 {
739 	if (s != NULL && *s != '\0' && (s[0] != '0' || s[1] != '\0'))
740 		return (1);
741 	return (0);
742 }
743 
744 /*
745  * Replace a key/value pair in buffer. #{blah} is expanded directly,
746  * #{?blah,a,b} is replace with a if blah exists and is nonzero else b.
747  */
748 static int
749 format_replace(struct format_tree *ft, const char *key, size_t keylen,
750     char **buf, size_t *len, size_t *off)
751 {
752 	char		*copy, *copy0, *endptr, *ptr, *found, *new, *value;
753 	char		*from = NULL, *to = NULL, *left, *right;
754 	size_t		 valuelen, newlen, fromlen, tolen, used;
755 	long		 limit = 0;
756 	int		 modifiers = 0, compare = 0;
757 
758 	/* Make a copy of the key. */
759 	copy0 = copy = xmalloc(keylen + 1);
760 	memcpy(copy, key, keylen);
761 	copy[keylen] = '\0';
762 
763 	/* Is there a length limit or whatnot? */
764 	switch (copy[0]) {
765 	case '!':
766 		if (copy[1] == '=' && copy[2] == ':') {
767 			compare = -1;
768 			copy += 3;
769 			break;
770 		}
771 		break;
772 	case '=':
773 		if (copy[1] == '=' && copy[2] == ':') {
774 			compare = 1;
775 			copy += 3;
776 			break;
777 		}
778 		errno = 0;
779 		limit = strtol(copy + 1, &endptr, 10);
780 		if (errno == ERANGE && (limit == LONG_MIN || limit == LONG_MAX))
781 			break;
782 		if (*endptr != ':')
783 			break;
784 		copy = endptr + 1;
785 		break;
786 	case 'b':
787 		if (copy[1] != ':')
788 			break;
789 		modifiers |= FORMAT_BASENAME;
790 		copy += 2;
791 		break;
792 	case 'd':
793 		if (copy[1] != ':')
794 			break;
795 		modifiers |= FORMAT_DIRNAME;
796 		copy += 2;
797 		break;
798 	case 't':
799 		if (copy[1] != ':')
800 			break;
801 		modifiers |= FORMAT_TIMESTRING;
802 		copy += 2;
803 		break;
804 	case 's':
805 		if (copy[1] != '/')
806 			break;
807 		from = copy + 2;
808 		for (copy = from; *copy != '\0' && *copy != '/'; copy++)
809 			/* nothing */;
810 		if (copy[0] != '/' || copy == from) {
811 			copy = copy0;
812 			break;
813 		}
814 		copy[0] = '\0';
815 		to = copy + 1;
816 		for (copy = to; *copy != '\0' && *copy != '/'; copy++)
817 			/* nothing */;
818 		if (copy[0] != '/' || copy[1] != ':') {
819 			copy = copy0;
820 			break;
821 		}
822 		copy[0] = '\0';
823 
824 		modifiers |= FORMAT_SUBSTITUTE;
825 		copy += 2;
826 		break;
827 	}
828 
829 	/* Is this a comparison or a conditional? */
830 	if (compare != 0) {
831 		/* Comparison: compare comma-separated left and right. */
832 		if (format_choose(copy, &left, &right) != 0)
833 			goto fail;
834 		left = format_expand(ft, left);
835 		right = format_expand(ft, right);
836 		if (compare == 1 && strcmp(left, right) == 0)
837 			value = xstrdup("1");
838 		else if (compare == -1 && strcmp(left, right) != 0)
839 			value = xstrdup("1");
840 		else
841 			value = xstrdup("0");
842 		free(right);
843 		free(left);
844 	} else if (*copy == '?') {
845 		/* Conditional: check first and choose second or third. */
846 		ptr = format_skip(copy);
847 		if (ptr == NULL)
848 			goto fail;
849 		*ptr = '\0';
850 
851 		found = format_find(ft, copy + 1, modifiers);
852 		if (found == NULL) {
853 			log_debug("XXX %s", copy + 1);
854 			found = format_expand(ft, copy + 1);}
855 		if (format_choose(ptr + 1, &left, &right) != 0)
856 			goto fail;
857 
858 		if (format_true(found))
859 			value = format_expand(ft, left);
860 		else
861 			value = format_expand(ft, right);
862 		free(found);
863 	} else {
864 		/* Neither: look up directly. */
865 		value = format_find(ft, copy, modifiers);
866 		if (value == NULL)
867 			value = xstrdup("");
868 	}
869 
870 	/* Perform substitution if any. */
871 	if (modifiers & FORMAT_SUBSTITUTE) {
872 		fromlen = strlen(from);
873 		tolen = strlen(to);
874 
875 		newlen = strlen(value) + 1;
876 		copy = new = xmalloc(newlen);
877 		for (ptr = value; *ptr != '\0'; /* nothing */) {
878 			if (strncmp(ptr, from, fromlen) != 0) {
879 				*new++ = *ptr++;
880 				continue;
881 			}
882 			used = new - copy;
883 
884 			newlen += tolen;
885 			copy = xrealloc(copy, newlen);
886 
887 			new = copy + used;
888 			memcpy(new, to, tolen);
889 
890 			new += tolen;
891 			ptr += fromlen;
892 		}
893 		*new = '\0';
894 		free(value);
895 		value = copy;
896 	}
897 
898 	/* Truncate the value if needed. */
899 	if (limit > 0) {
900 		new = utf8_trimcstr(value, limit);
901 		free(value);
902 		value = new;
903 	} else if (limit < 0) {
904 		new = utf8_rtrimcstr(value, -limit);
905 		free(value);
906 		value = new;
907 	}
908 
909 	/* Expand the buffer and copy in the value. */
910 	valuelen = strlen(value);
911 	while (*len - *off < valuelen + 1) {
912 		*buf = xreallocarray(*buf, 2, *len);
913 		*len *= 2;
914 	}
915 	memcpy(*buf + *off, value, valuelen);
916 	*off += valuelen;
917 
918 	free(value);
919 	free(copy0);
920 	return (0);
921 
922 fail:
923 	free(copy0);
924 	return (-1);
925 }
926 
927 /* Expand keys in a template, passing through strftime first. */
928 char *
929 format_expand_time(struct format_tree *ft, const char *fmt, time_t t)
930 {
931 	struct tm	*tm;
932 	char		 s[2048];
933 
934 	if (fmt == NULL || *fmt == '\0')
935 		return (xstrdup(""));
936 
937 	tm = localtime(&t);
938 
939 	if (strftime(s, sizeof s, fmt, tm) == 0)
940 		return (xstrdup(""));
941 
942 	return (format_expand(ft, s));
943 }
944 
945 /* Expand keys in a template. */
946 char *
947 format_expand(struct format_tree *ft, const char *fmt)
948 {
949 	char		*buf, *out;
950 	const char	*ptr, *s, *saved = fmt;
951 	size_t		 off, len, n, outlen;
952 	int     	 ch, brackets;
953 
954 	if (fmt == NULL)
955 		return (xstrdup(""));
956 
957 	len = 64;
958 	buf = xmalloc(len);
959 	off = 0;
960 
961 	while (*fmt != '\0') {
962 		if (*fmt != '#') {
963 			while (len - off < 2) {
964 				buf = xreallocarray(buf, 2, len);
965 				len *= 2;
966 			}
967 			buf[off++] = *fmt++;
968 			continue;
969 		}
970 		fmt++;
971 
972 		ch = (u_char) *fmt++;
973 		switch (ch) {
974 		case '(':
975 			brackets = 1;
976 			for (ptr = fmt; *ptr != '\0'; ptr++) {
977 				if (*ptr == '(')
978 					brackets++;
979 				if (*ptr == ')' && --brackets == 0)
980 					break;
981 			}
982 			if (*ptr != ')' || brackets != 0)
983 				break;
984 			n = ptr - fmt;
985 
986 			if (ft->flags & FORMAT_NOJOBS)
987 				out = xstrdup("");
988 			else
989 				out = format_job_get(ft, xstrndup(fmt, n));
990 			outlen = strlen(out);
991 
992 			while (len - off < outlen + 1) {
993 				buf = xreallocarray(buf, 2, len);
994 				len *= 2;
995 			}
996 			memcpy(buf + off, out, outlen);
997 			off += outlen;
998 
999 			free(out);
1000 
1001 			fmt += n + 1;
1002 			continue;
1003 		case '{':
1004 			brackets = 1;
1005 			for (ptr = fmt; *ptr != '\0'; ptr++) {
1006 				if (*ptr == '{')
1007 					brackets++;
1008 				if (*ptr == '}' && --brackets == 0)
1009 					break;
1010 			}
1011 			if (*ptr != '}' || brackets != 0)
1012 				break;
1013 			n = ptr - fmt;
1014 
1015 			if (format_replace(ft, fmt, n, &buf, &len, &off) != 0)
1016 				break;
1017 			fmt += n + 1;
1018 			continue;
1019 		case '#':
1020 			while (len - off < 2) {
1021 				buf = xreallocarray(buf, 2, len);
1022 				len *= 2;
1023 			}
1024 			buf[off++] = '#';
1025 			continue;
1026 		default:
1027 			s = NULL;
1028 			if (ch >= 'A' && ch <= 'Z')
1029 				s = format_upper[ch - 'A'];
1030 			else if (ch >= 'a' && ch <= 'z')
1031 				s = format_lower[ch - 'a'];
1032 			if (s == NULL) {
1033 				while (len - off < 3) {
1034 					buf = xreallocarray(buf, 2, len);
1035 					len *= 2;
1036 				}
1037 				buf[off++] = '#';
1038 				buf[off++] = ch;
1039 				continue;
1040 			}
1041 			n = strlen(s);
1042 			if (format_replace(ft, s, n, &buf, &len, &off) != 0)
1043 				break;
1044 			continue;
1045 		}
1046 
1047 		break;
1048 	}
1049 	buf[off] = '\0';
1050 
1051 	log_debug("format '%s' -> '%s'", saved, buf);
1052 	return (buf);
1053 }
1054 
1055 /* Set defaults for any of arguments that are not NULL. */
1056 void
1057 format_defaults(struct format_tree *ft, struct client *c, struct session *s,
1058     struct winlink *wl, struct window_pane *wp)
1059 {
1060 	if (s == NULL && c != NULL)
1061 		s = c->session;
1062 	if (wl == NULL && s != NULL)
1063 		wl = s->curw;
1064 	if (wp == NULL && wl != NULL)
1065 		wp = wl->window->active;
1066 
1067 	if (c != NULL)
1068 		format_defaults_client(ft, c);
1069 	if (s != NULL)
1070 		format_defaults_session(ft, s);
1071 	if (s != NULL && wl != NULL)
1072 		format_defaults_winlink(ft, s, wl);
1073 	if (wp != NULL)
1074 		format_defaults_pane(ft, wp);
1075 }
1076 
1077 /* Set default format keys for a session. */
1078 static void
1079 format_defaults_session(struct format_tree *ft, struct session *s)
1080 {
1081 	struct session_group	*sg;
1082 
1083 	ft->s = s;
1084 
1085 	format_add(ft, "session_name", "%s", s->name);
1086 	format_add(ft, "session_windows", "%u", winlink_count(&s->windows));
1087 	format_add(ft, "session_width", "%u", s->sx);
1088 	format_add(ft, "session_height", "%u", s->sy);
1089 	format_add(ft, "session_id", "$%u", s->id);
1090 
1091 	sg = session_group_find(s);
1092 	format_add(ft, "session_grouped", "%d", sg != NULL);
1093 	if (sg != NULL)
1094 		format_add(ft, "session_group", "%u", session_group_index(sg));
1095 
1096 	format_add_tv(ft, "session_created", &s->creation_time);
1097 	format_add_tv(ft, "session_last_attached", &s->last_attached_time);
1098 	format_add_tv(ft, "session_activity", &s->activity_time);
1099 
1100 	format_add(ft, "session_attached", "%u", s->attached);
1101 	format_add(ft, "session_many_attached", "%d", s->attached > 1);
1102 
1103 	format_add_cb(ft, "session_alerts", format_cb_session_alerts);
1104 }
1105 
1106 /* Set default format keys for a client. */
1107 static void
1108 format_defaults_client(struct format_tree *ft, struct client *c)
1109 {
1110 	struct session	*s;
1111 	const char	*name;
1112 	struct tty	*tty = &c->tty;
1113 	const char	*types[] = TTY_TYPES;
1114 
1115 	if (ft->s == NULL)
1116 		ft->s = c->session;
1117 
1118 	format_add(ft, "client_pid", "%ld", (long) c->pid);
1119 	format_add(ft, "client_height", "%u", tty->sy);
1120 	format_add(ft, "client_width", "%u", tty->sx);
1121 	if (tty->path != NULL)
1122 		format_add(ft, "client_tty", "%s", tty->path);
1123 	format_add(ft, "client_control_mode", "%d",
1124 		!!(c->flags & CLIENT_CONTROL));
1125 
1126 	if (tty->term_name != NULL)
1127 		format_add(ft, "client_termname", "%s", tty->term_name);
1128 	if (tty->term_name != NULL)
1129 		format_add(ft, "client_termtype", "%s", types[tty->term_type]);
1130 
1131 	format_add_tv(ft, "client_created", &c->creation_time);
1132 	format_add_tv(ft, "client_activity", &c->activity_time);
1133 
1134 	name = server_client_get_key_table(c);
1135 	if (strcmp(c->keytable->name, name) == 0)
1136 		format_add(ft, "client_prefix", "%d", 0);
1137 	else
1138 		format_add(ft, "client_prefix", "%d", 1);
1139 	format_add(ft, "client_key_table", "%s", c->keytable->name);
1140 
1141 	if (tty->flags & TTY_UTF8)
1142 		format_add(ft, "client_utf8", "%d", 1);
1143 	else
1144 		format_add(ft, "client_utf8", "%d", 0);
1145 
1146 	if (c->flags & CLIENT_READONLY)
1147 		format_add(ft, "client_readonly", "%d", 1);
1148 	else
1149 		format_add(ft, "client_readonly", "%d", 0);
1150 
1151 	s = c->session;
1152 	if (s != NULL)
1153 		format_add(ft, "client_session", "%s", s->name);
1154 	s = c->last_session;
1155 	if (s != NULL && session_alive(s))
1156 		format_add(ft, "client_last_session", "%s", s->name);
1157 }
1158 
1159 /* Set default format keys for a window. */
1160 void
1161 format_defaults_window(struct format_tree *ft, struct window *w)
1162 {
1163 	ft->w = w;
1164 
1165 	format_add_tv(ft, "window_activity", &w->activity_time);
1166 	format_add(ft, "window_id", "@%u", w->id);
1167 	format_add(ft, "window_name", "%s", w->name);
1168 	format_add(ft, "window_width", "%u", w->sx);
1169 	format_add(ft, "window_height", "%u", w->sy);
1170 	format_add_cb(ft, "window_layout", format_cb_window_layout);
1171 	format_add_cb(ft, "window_visible_layout",
1172 	    format_cb_window_visible_layout);
1173 	format_add(ft, "window_panes", "%u", window_count_panes(w));
1174 	format_add(ft, "window_zoomed_flag", "%d",
1175 	    !!(w->flags & WINDOW_ZOOMED));
1176 }
1177 
1178 /* Set default format keys for a winlink. */
1179 static void
1180 format_defaults_winlink(struct format_tree *ft, struct session *s,
1181     struct winlink *wl)
1182 {
1183 	struct window	*w = wl->window;
1184 	char		*flags;
1185 
1186 	if (ft->w == NULL)
1187 		ft->w = wl->window;
1188 
1189 	flags = window_printable_flags(s, wl);
1190 
1191 	format_defaults_window(ft, w);
1192 
1193 	format_add(ft, "window_index", "%d", wl->idx);
1194 	format_add(ft, "window_flags", "%s", flags);
1195 	format_add(ft, "window_active", "%d", wl == s->curw);
1196 
1197 	format_add(ft, "window_bell_flag", "%d",
1198 	    !!(wl->flags & WINLINK_BELL));
1199 	format_add(ft, "window_activity_flag", "%d",
1200 	    !!(wl->flags & WINLINK_ACTIVITY));
1201 	format_add(ft, "window_silence_flag", "%d",
1202 	    !!(wl->flags & WINLINK_SILENCE));
1203 	format_add(ft, "window_last_flag", "%d",
1204 	    !!(wl == TAILQ_FIRST(&s->lastw)));
1205 	format_add(ft, "window_linked", "%d", session_is_linked(s, wl->window));
1206 
1207 	free(flags);
1208 }
1209 
1210 /* Set default format keys for a window pane. */
1211 void
1212 format_defaults_pane(struct format_tree *ft, struct window_pane *wp)
1213 {
1214 	struct grid	*gd = wp->base.grid;
1215 	u_int		 idx;
1216 	int  		 status, scroll_position;
1217 
1218 	if (ft->w == NULL)
1219 		ft->w = wp->window;
1220 	ft->wp = wp;
1221 
1222 	format_add(ft, "history_size", "%u", gd->hsize);
1223 	format_add(ft, "history_limit", "%u", gd->hlimit);
1224 	format_add_cb(ft, "history_bytes", format_cb_history_bytes);
1225 
1226 	if (window_pane_index(wp, &idx) != 0)
1227 		fatalx("index not found");
1228 	format_add(ft, "pane_index", "%u", idx);
1229 
1230 	format_add(ft, "pane_width", "%u", wp->sx);
1231 	format_add(ft, "pane_height", "%u", wp->sy);
1232 	format_add(ft, "pane_title", "%s", wp->base.title);
1233 	format_add(ft, "pane_id", "%%%u", wp->id);
1234 	format_add(ft, "pane_active", "%d", wp == wp->window->active);
1235 	format_add(ft, "pane_input_off", "%d", !!(wp->flags & PANE_INPUTOFF));
1236 
1237 	status = wp->status;
1238 	if (wp->fd == -1 && WIFEXITED(status))
1239 		format_add(ft, "pane_dead_status", "%d", WEXITSTATUS(status));
1240 	format_add(ft, "pane_dead", "%d", wp->fd == -1);
1241 
1242 	if (window_pane_visible(wp)) {
1243 		format_add(ft, "pane_left", "%u", wp->xoff);
1244 		format_add(ft, "pane_top", "%u", wp->yoff);
1245 		format_add(ft, "pane_right", "%u", wp->xoff + wp->sx - 1);
1246 		format_add(ft, "pane_bottom", "%u", wp->yoff + wp->sy - 1);
1247 	}
1248 
1249 	format_add(ft, "pane_in_mode", "%d", wp->screen != &wp->base);
1250 	format_add(ft, "pane_synchronized", "%d",
1251 	    !!options_get_number(wp->window->options, "synchronize-panes"));
1252 
1253 	format_add(ft, "pane_tty", "%s", wp->tty);
1254 	format_add(ft, "pane_pid", "%ld", (long) wp->pid);
1255 	format_add_cb(ft, "pane_start_command", format_cb_start_command);
1256 	format_add_cb(ft, "pane_current_command", format_cb_current_command);
1257 
1258 	format_add(ft, "cursor_x", "%u", wp->base.cx);
1259 	format_add(ft, "cursor_y", "%u", wp->base.cy);
1260 	format_add(ft, "scroll_region_upper", "%u", wp->base.rupper);
1261 	format_add(ft, "scroll_region_lower", "%u", wp->base.rlower);
1262 
1263 	scroll_position = window_copy_scroll_position(wp);
1264 	if (scroll_position != -1)
1265 		format_add(ft, "scroll_position", "%d", scroll_position);
1266 
1267 	format_add(ft, "alternate_on", "%d", wp->saved_grid ? 1 : 0);
1268 	format_add(ft, "alternate_saved_x", "%u", wp->saved_cx);
1269 	format_add(ft, "alternate_saved_y", "%u", wp->saved_cy);
1270 
1271 	format_add(ft, "cursor_flag", "%d",
1272 	    !!(wp->base.mode & MODE_CURSOR));
1273 	format_add(ft, "insert_flag", "%d",
1274 	    !!(wp->base.mode & MODE_INSERT));
1275 	format_add(ft, "keypad_cursor_flag", "%d",
1276 	    !!(wp->base.mode & MODE_KCURSOR));
1277 	format_add(ft, "keypad_flag", "%d",
1278 	    !!(wp->base.mode & MODE_KKEYPAD));
1279 	format_add(ft, "wrap_flag", "%d",
1280 	    !!(wp->base.mode & MODE_WRAP));
1281 
1282 	format_add(ft, "mouse_any_flag", "%d",
1283 	    !!(wp->base.mode & (MODE_MOUSE_STANDARD|MODE_MOUSE_BUTTON)));
1284 	format_add(ft, "mouse_standard_flag", "%d",
1285 	    !!(wp->base.mode & MODE_MOUSE_STANDARD));
1286 	format_add(ft, "mouse_button_flag", "%d",
1287 	    !!(wp->base.mode & MODE_MOUSE_BUTTON));
1288 
1289 	format_add_cb(ft, "pane_tabs", format_cb_pane_tabs);
1290 }
1291 
1292 /* Set default format keys for paste buffer. */
1293 void
1294 format_defaults_paste_buffer(struct format_tree *ft, struct paste_buffer *pb)
1295 {
1296 	size_t	 bufsize;
1297 	char	*s;
1298 
1299 	paste_buffer_data(pb, &bufsize);
1300 	format_add(ft, "buffer_size", "%zu", bufsize);
1301 	format_add(ft, "buffer_name", "%s", paste_buffer_name(pb));
1302 
1303 	s = paste_make_sample(pb);
1304 	format_add(ft, "buffer_sample", "%s", s);
1305 	free(s);
1306 }
1307