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