1 | /* $OpenBSD$ */ |
2 | |
3 | /* |
4 | * Copyright (c) 2011 Nicholas Marriott <[email protected]> |
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 <fnmatch.h> |
25 | #include <libgen.h> |
26 | #include <regex.h> |
27 | #include <stdarg.h> |
28 | #include <stdlib.h> |
29 | #include <string.h> |
30 | #include <time.h> |
31 | #include <unistd.h> |
32 | |
33 | #include "tmux.h" |
34 | |
35 | /* |
36 | * Build a list of key-value pairs and use them to expand #{key} entries in a |
37 | * string. |
38 | */ |
39 | |
40 | struct format_entry; |
41 | typedef void (*format_cb)(struct format_tree *, struct format_entry *); |
42 | |
43 | static char *format_job_get(struct format_tree *, const char *); |
44 | static void format_job_timer(int, short, void *); |
45 | |
46 | static char *format_find(struct format_tree *, const char *, int); |
47 | static void format_add_cb(struct format_tree *, const char *, format_cb); |
48 | static void format_add_tv(struct format_tree *, const char *, |
49 | struct timeval *); |
50 | static int format_replace(struct format_tree *, const char *, size_t, |
51 | char **, size_t *, size_t *); |
52 | |
53 | static void format_defaults_session(struct format_tree *, |
54 | struct session *); |
55 | static void format_defaults_client(struct format_tree *, struct client *); |
56 | static void format_defaults_winlink(struct format_tree *, struct winlink *); |
57 | |
58 | /* Entry in format job tree. */ |
59 | struct format_job { |
60 | struct client *client; |
61 | u_int tag; |
62 | const char *cmd; |
63 | const char *expanded; |
64 | |
65 | time_t last; |
66 | char *out; |
67 | int updated; |
68 | |
69 | struct job *job; |
70 | int status; |
71 | |
72 | RB_ENTRY(format_job) entry; |
73 | }; |
74 | |
75 | /* Format job tree. */ |
76 | static struct event format_job_event; |
77 | static int format_job_cmp(struct format_job *, struct format_job *); |
78 | static RB_HEAD(format_job_tree, format_job) format_jobs = RB_INITIALIZER(); |
79 | RB_GENERATE_STATIC(format_job_tree, format_job, entry, format_job_cmp); |
80 | |
81 | /* Format job tree comparison function. */ |
82 | static int |
83 | format_job_cmp(struct format_job *fj1, struct format_job *fj2) |
84 | { |
85 | if (fj1->tag < fj2->tag) |
86 | return (-1); |
87 | if (fj1->tag > fj2->tag) |
88 | return (1); |
89 | return (strcmp(fj1->cmd, fj2->cmd)); |
90 | } |
91 | |
92 | /* Format modifiers. */ |
93 | #define FORMAT_TIMESTRING 0x1 |
94 | #define FORMAT_BASENAME 0x2 |
95 | #define FORMAT_DIRNAME 0x4 |
96 | #define FORMAT_QUOTE 0x8 |
97 | #define FORMAT_LITERAL 0x10 |
98 | #define FORMAT_EXPAND 0x20 |
99 | #define FORMAT_EXPANDTIME 0x40 |
100 | #define FORMAT_SESSIONS 0x80 |
101 | #define FORMAT_WINDOWS 0x100 |
102 | #define FORMAT_PANES 0x200 |
103 | |
104 | /* Limit on recursion. */ |
105 | #define FORMAT_LOOP_LIMIT 10 |
106 | |
107 | /* Entry in format tree. */ |
108 | struct format_entry { |
109 | char *key; |
110 | char *value; |
111 | time_t t; |
112 | format_cb cb; |
113 | RB_ENTRY(format_entry) entry; |
114 | }; |
115 | |
116 | /* Format entry tree. */ |
117 | struct format_tree { |
118 | struct client *c; |
119 | struct session *s; |
120 | struct winlink *wl; |
121 | struct window *w; |
122 | struct window_pane *wp; |
123 | |
124 | struct cmdq_item *item; |
125 | struct client *client; |
126 | u_int tag; |
127 | int flags; |
128 | time_t time; |
129 | u_int loop; |
130 | |
131 | struct mouse_event m; |
132 | |
133 | RB_HEAD(format_entry_tree, format_entry) tree; |
134 | }; |
135 | static int format_entry_cmp(struct format_entry *, struct format_entry *); |
136 | RB_GENERATE_STATIC(format_entry_tree, format_entry, entry, format_entry_cmp); |
137 | |
138 | /* Format modifier. */ |
139 | struct format_modifier { |
140 | char modifier[3]; |
141 | u_int size; |
142 | |
143 | char **argv; |
144 | int argc; |
145 | }; |
146 | |
147 | /* Format entry tree comparison function. */ |
148 | static int |
149 | format_entry_cmp(struct format_entry *fe1, struct format_entry *fe2) |
150 | { |
151 | return (strcmp(fe1->key, fe2->key)); |
152 | } |
153 | |
154 | /* Single-character uppercase aliases. */ |
155 | static const char *format_upper[] = { |
156 | NULL, /* A */ |
157 | NULL, /* B */ |
158 | NULL, /* C */ |
159 | "pane_id" , /* D */ |
160 | NULL, /* E */ |
161 | "window_flags" , /* F */ |
162 | NULL, /* G */ |
163 | "host" , /* H */ |
164 | "window_index" , /* I */ |
165 | NULL, /* J */ |
166 | NULL, /* K */ |
167 | NULL, /* L */ |
168 | NULL, /* M */ |
169 | NULL, /* N */ |
170 | NULL, /* O */ |
171 | "pane_index" , /* P */ |
172 | NULL, /* Q */ |
173 | NULL, /* R */ |
174 | "session_name" , /* S */ |
175 | "pane_title" , /* T */ |
176 | NULL, /* U */ |
177 | NULL, /* V */ |
178 | "window_name" , /* W */ |
179 | NULL, /* X */ |
180 | NULL, /* Y */ |
181 | NULL /* Z */ |
182 | }; |
183 | |
184 | /* Single-character lowercase aliases. */ |
185 | static const char *format_lower[] = { |
186 | NULL, /* a */ |
187 | NULL, /* b */ |
188 | NULL, /* c */ |
189 | NULL, /* d */ |
190 | NULL, /* e */ |
191 | NULL, /* f */ |
192 | NULL, /* g */ |
193 | "host_short" , /* h */ |
194 | NULL, /* i */ |
195 | NULL, /* j */ |
196 | NULL, /* k */ |
197 | NULL, /* l */ |
198 | NULL, /* m */ |
199 | NULL, /* n */ |
200 | NULL, /* o */ |
201 | NULL, /* p */ |
202 | NULL, /* q */ |
203 | NULL, /* r */ |
204 | NULL, /* s */ |
205 | NULL, /* t */ |
206 | NULL, /* u */ |
207 | NULL, /* v */ |
208 | NULL, /* w */ |
209 | NULL, /* x */ |
210 | NULL, /* y */ |
211 | NULL /* z */ |
212 | }; |
213 | |
214 | /* Is logging enabled? */ |
215 | static inline int |
216 | format_logging(struct format_tree *ft) |
217 | { |
218 | return (log_get_level() != 0 || (ft->flags & FORMAT_VERBOSE)); |
219 | } |
220 | |
221 | /* Log a message if verbose. */ |
222 | static void printflike(3, 4) |
223 | format_log1(struct format_tree *ft, const char *from, const char *fmt, ...) |
224 | { |
225 | va_list ap; |
226 | char *s; |
227 | static const char spaces[] = " " ; |
228 | |
229 | if (!format_logging(ft)) |
230 | return; |
231 | |
232 | va_start(ap, fmt); |
233 | vasprintf(&s, fmt, ap); |
234 | va_end(ap); |
235 | |
236 | log_debug("%s: %s" , from, s); |
237 | if (ft->item != NULL && (ft->flags & FORMAT_VERBOSE)) |
238 | cmdq_print(ft->item, "#%.*s%s" , ft->loop, spaces, s); |
239 | |
240 | free(s); |
241 | } |
242 | #define format_log(ft, fmt, ...) format_log1(ft, __func__, fmt, ##__VA_ARGS__) |
243 | |
244 | /* Format job update callback. */ |
245 | static void |
246 | format_job_update(struct job *job) |
247 | { |
248 | struct format_job *fj = job_get_data(job); |
249 | struct evbuffer *evb = job_get_event(job)->input; |
250 | char *line = NULL, *next; |
251 | time_t t; |
252 | |
253 | while ((next = evbuffer_readline(evb)) != NULL) { |
254 | free(line); |
255 | line = next; |
256 | } |
257 | if (line == NULL) |
258 | return; |
259 | fj->updated = 1; |
260 | |
261 | free(fj->out); |
262 | fj->out = line; |
263 | |
264 | log_debug("%s: %p %s: %s" , __func__, fj, fj->cmd, fj->out); |
265 | |
266 | t = time(NULL); |
267 | if (fj->status && fj->last != t) { |
268 | if (fj->client != NULL) |
269 | server_status_client(fj->client); |
270 | fj->last = t; |
271 | } |
272 | } |
273 | |
274 | /* Format job complete callback. */ |
275 | static void |
276 | format_job_complete(struct job *job) |
277 | { |
278 | struct format_job *fj = job_get_data(job); |
279 | struct evbuffer *evb = job_get_event(job)->input; |
280 | char *line, *buf; |
281 | size_t len; |
282 | |
283 | fj->job = NULL; |
284 | |
285 | buf = NULL; |
286 | if ((line = evbuffer_readline(evb)) == NULL) { |
287 | len = EVBUFFER_LENGTH(evb); |
288 | buf = xmalloc(len + 1); |
289 | if (len != 0) |
290 | memcpy(buf, EVBUFFER_DATA(evb), len); |
291 | buf[len] = '\0'; |
292 | } else |
293 | buf = line; |
294 | |
295 | log_debug("%s: %p %s: %s" , __func__, fj, fj->cmd, buf); |
296 | |
297 | if (*buf != '\0' || !fj->updated) { |
298 | free(fj->out); |
299 | fj->out = buf; |
300 | } else |
301 | free(buf); |
302 | |
303 | if (fj->status) { |
304 | if (fj->client != NULL) |
305 | server_status_client(fj->client); |
306 | fj->status = 0; |
307 | } |
308 | } |
309 | |
310 | /* Find a job. */ |
311 | static char * |
312 | format_job_get(struct format_tree *ft, const char *cmd) |
313 | { |
314 | struct format_job_tree *jobs; |
315 | struct format_job fj0, *fj; |
316 | time_t t; |
317 | char *expanded; |
318 | int force; |
319 | |
320 | if (ft->client == NULL) |
321 | jobs = &format_jobs; |
322 | else if (ft->client->jobs != NULL) |
323 | jobs = ft->client->jobs; |
324 | else { |
325 | jobs = ft->client->jobs = xmalloc(sizeof *ft->client->jobs); |
326 | RB_INIT(jobs); |
327 | } |
328 | |
329 | fj0.tag = ft->tag; |
330 | fj0.cmd = cmd; |
331 | if ((fj = RB_FIND(format_job_tree, jobs, &fj0)) == NULL) { |
332 | fj = xcalloc(1, sizeof *fj); |
333 | fj->client = ft->client; |
334 | fj->tag = ft->tag; |
335 | fj->cmd = xstrdup(cmd); |
336 | fj->expanded = NULL; |
337 | |
338 | xasprintf(&fj->out, "<'%s' not ready>" , fj->cmd); |
339 | |
340 | RB_INSERT(format_job_tree, jobs, fj); |
341 | } |
342 | |
343 | expanded = format_expand(ft, cmd); |
344 | if (fj->expanded == NULL || strcmp(expanded, fj->expanded) != 0) { |
345 | free((void *)fj->expanded); |
346 | fj->expanded = xstrdup(expanded); |
347 | force = 1; |
348 | } else |
349 | force = (ft->flags & FORMAT_FORCE); |
350 | |
351 | t = time(NULL); |
352 | if (force && fj->job != NULL) |
353 | job_free(fj->job); |
354 | if (force || (fj->job == NULL && fj->last != t)) { |
355 | fj->job = job_run(expanded, NULL, |
356 | server_client_get_cwd(ft->client, NULL), format_job_update, |
357 | format_job_complete, NULL, fj, JOB_NOWAIT); |
358 | if (fj->job == NULL) { |
359 | free(fj->out); |
360 | xasprintf(&fj->out, "<'%s' didn't start>" , fj->cmd); |
361 | } |
362 | fj->last = t; |
363 | fj->updated = 0; |
364 | } |
365 | |
366 | if (ft->flags & FORMAT_STATUS) |
367 | fj->status = 1; |
368 | |
369 | free(expanded); |
370 | return (format_expand(ft, fj->out)); |
371 | } |
372 | |
373 | /* Remove old jobs. */ |
374 | static void |
375 | format_job_tidy(struct format_job_tree *jobs, int force) |
376 | { |
377 | struct format_job *fj, *fj1; |
378 | time_t now; |
379 | |
380 | now = time(NULL); |
381 | RB_FOREACH_SAFE(fj, format_job_tree, jobs, fj1) { |
382 | if (!force && (fj->last > now || now - fj->last < 3600)) |
383 | continue; |
384 | RB_REMOVE(format_job_tree, jobs, fj); |
385 | |
386 | log_debug("%s: %s" , __func__, fj->cmd); |
387 | |
388 | if (fj->job != NULL) |
389 | job_free(fj->job); |
390 | |
391 | free((void *)fj->expanded); |
392 | free((void *)fj->cmd); |
393 | free(fj->out); |
394 | |
395 | free(fj); |
396 | } |
397 | } |
398 | |
399 | /* Remove old jobs for client. */ |
400 | void |
401 | format_lost_client(struct client *c) |
402 | { |
403 | if (c->jobs != NULL) |
404 | format_job_tidy(c->jobs, 1); |
405 | free(c->jobs); |
406 | } |
407 | |
408 | /* Remove old jobs periodically. */ |
409 | static void |
410 | format_job_timer(__unused int fd, __unused short events, __unused void *arg) |
411 | { |
412 | struct client *c; |
413 | struct timeval tv = { .tv_sec = 60 }; |
414 | |
415 | format_job_tidy(&format_jobs, 0); |
416 | TAILQ_FOREACH(c, &clients, entry) { |
417 | if (c->jobs != NULL) |
418 | format_job_tidy(c->jobs, 0); |
419 | } |
420 | |
421 | evtimer_del(&format_job_event); |
422 | evtimer_add(&format_job_event, &tv); |
423 | } |
424 | |
425 | /* Callback for host. */ |
426 | static void |
427 | format_cb_host(__unused struct format_tree *ft, struct format_entry *fe) |
428 | { |
429 | char host[HOST_NAME_MAX + 1]; |
430 | |
431 | if (gethostname(host, sizeof host) != 0) |
432 | fe->value = xstrdup("" ); |
433 | else |
434 | fe->value = xstrdup(host); |
435 | } |
436 | |
437 | /* Callback for host_short. */ |
438 | static void |
439 | format_cb_host_short(__unused struct format_tree *ft, struct format_entry *fe) |
440 | { |
441 | char host[HOST_NAME_MAX + 1], *cp; |
442 | |
443 | if (gethostname(host, sizeof host) != 0) |
444 | fe->value = xstrdup("" ); |
445 | else { |
446 | if ((cp = strchr(host, '.')) != NULL) |
447 | *cp = '\0'; |
448 | fe->value = xstrdup(host); |
449 | } |
450 | } |
451 | |
452 | /* Callback for pid. */ |
453 | static void |
454 | format_cb_pid(__unused struct format_tree *ft, struct format_entry *fe) |
455 | { |
456 | xasprintf(&fe->value, "%ld" , (long)getpid()); |
457 | } |
458 | |
459 | /* Callback for session_attached_list. */ |
460 | static void |
461 | format_cb_session_attached_list(struct format_tree *ft, struct format_entry *fe) |
462 | { |
463 | struct session *s = ft->s; |
464 | struct client *loop; |
465 | struct evbuffer *buffer; |
466 | int size; |
467 | |
468 | if (s == NULL) |
469 | return; |
470 | |
471 | buffer = evbuffer_new(); |
472 | if (buffer == NULL) |
473 | fatalx("out of memory" ); |
474 | |
475 | TAILQ_FOREACH(loop, &clients, entry) { |
476 | if (loop->session == s) { |
477 | if (EVBUFFER_LENGTH(buffer) > 0) |
478 | evbuffer_add(buffer, "," , 1); |
479 | evbuffer_add_printf(buffer, "%s" , loop->name); |
480 | } |
481 | } |
482 | |
483 | if ((size = EVBUFFER_LENGTH(buffer)) != 0) |
484 | xasprintf(&fe->value, "%.*s" , size, EVBUFFER_DATA(buffer)); |
485 | evbuffer_free(buffer); |
486 | } |
487 | |
488 | /* Callback for session_alerts. */ |
489 | static void |
490 | format_cb_session_alerts(struct format_tree *ft, struct format_entry *fe) |
491 | { |
492 | struct session *s = ft->s; |
493 | struct winlink *wl; |
494 | char alerts[1024], tmp[16]; |
495 | |
496 | if (s == NULL) |
497 | return; |
498 | |
499 | *alerts = '\0'; |
500 | RB_FOREACH(wl, winlinks, &s->windows) { |
501 | if ((wl->flags & WINLINK_ALERTFLAGS) == 0) |
502 | continue; |
503 | xsnprintf(tmp, sizeof tmp, "%u" , wl->idx); |
504 | |
505 | if (*alerts != '\0') |
506 | strlcat(alerts, "," , sizeof alerts); |
507 | strlcat(alerts, tmp, sizeof alerts); |
508 | if (wl->flags & WINLINK_ACTIVITY) |
509 | strlcat(alerts, "#" , sizeof alerts); |
510 | if (wl->flags & WINLINK_BELL) |
511 | strlcat(alerts, "!" , sizeof alerts); |
512 | if (wl->flags & WINLINK_SILENCE) |
513 | strlcat(alerts, "~" , sizeof alerts); |
514 | } |
515 | fe->value = xstrdup(alerts); |
516 | } |
517 | |
518 | /* Callback for session_stack. */ |
519 | static void |
520 | format_cb_session_stack(struct format_tree *ft, struct format_entry *fe) |
521 | { |
522 | struct session *s = ft->s; |
523 | struct winlink *wl; |
524 | char result[1024], tmp[16]; |
525 | |
526 | if (s == NULL) |
527 | return; |
528 | |
529 | xsnprintf(result, sizeof result, "%u" , s->curw->idx); |
530 | TAILQ_FOREACH(wl, &s->lastw, sentry) { |
531 | xsnprintf(tmp, sizeof tmp, "%u" , wl->idx); |
532 | |
533 | if (*result != '\0') |
534 | strlcat(result, "," , sizeof result); |
535 | strlcat(result, tmp, sizeof result); |
536 | } |
537 | fe->value = xstrdup(result); |
538 | } |
539 | |
540 | /* Callback for window_stack_index. */ |
541 | static void |
542 | format_cb_window_stack_index(struct format_tree *ft, struct format_entry *fe) |
543 | { |
544 | struct session *s = ft->wl->session; |
545 | struct winlink *wl; |
546 | u_int idx; |
547 | |
548 | idx = 0; |
549 | TAILQ_FOREACH(wl, &s->lastw, sentry) { |
550 | idx++; |
551 | if (wl == ft->wl) |
552 | break; |
553 | } |
554 | if (wl != NULL) |
555 | xasprintf(&fe->value, "%u" , idx); |
556 | else |
557 | fe->value = xstrdup("0" ); |
558 | } |
559 | |
560 | /* Callback for window_linked_sessions_list. */ |
561 | static void |
562 | format_cb_window_linked_sessions_list(struct format_tree *ft, |
563 | struct format_entry *fe) |
564 | { |
565 | struct window *w = ft->wl->window; |
566 | struct winlink *wl; |
567 | struct evbuffer *buffer; |
568 | int size; |
569 | |
570 | buffer = evbuffer_new(); |
571 | if (buffer == NULL) |
572 | fatalx("out of memory" ); |
573 | |
574 | TAILQ_FOREACH(wl, &w->winlinks, wentry) { |
575 | if (EVBUFFER_LENGTH(buffer) > 0) |
576 | evbuffer_add(buffer, "," , 1); |
577 | evbuffer_add_printf(buffer, "%s" , wl->session->name); |
578 | } |
579 | |
580 | if ((size = EVBUFFER_LENGTH(buffer)) != 0) |
581 | xasprintf(&fe->value, "%.*s" , size, EVBUFFER_DATA(buffer)); |
582 | evbuffer_free(buffer); |
583 | } |
584 | |
585 | /* Callback for window_active_sessions. */ |
586 | static void |
587 | format_cb_window_active_sessions(struct format_tree *ft, |
588 | struct format_entry *fe) |
589 | { |
590 | struct window *w = ft->wl->window; |
591 | struct winlink *wl; |
592 | u_int n = 0; |
593 | |
594 | TAILQ_FOREACH(wl, &w->winlinks, wentry) { |
595 | if (wl->session->curw == wl) |
596 | n++; |
597 | } |
598 | |
599 | xasprintf(&fe->value, "%u" , n); |
600 | } |
601 | |
602 | /* Callback for window_active_sessions_list. */ |
603 | static void |
604 | format_cb_window_active_sessions_list(struct format_tree *ft, |
605 | struct format_entry *fe) |
606 | { |
607 | struct window *w = ft->wl->window; |
608 | struct winlink *wl; |
609 | struct evbuffer *buffer; |
610 | int size; |
611 | |
612 | buffer = evbuffer_new(); |
613 | if (buffer == NULL) |
614 | fatalx("out of memory" ); |
615 | |
616 | TAILQ_FOREACH(wl, &w->winlinks, wentry) { |
617 | if (wl->session->curw == wl) { |
618 | if (EVBUFFER_LENGTH(buffer) > 0) |
619 | evbuffer_add(buffer, "," , 1); |
620 | evbuffer_add_printf(buffer, "%s" , wl->session->name); |
621 | } |
622 | } |
623 | |
624 | if ((size = EVBUFFER_LENGTH(buffer)) != 0) |
625 | xasprintf(&fe->value, "%.*s" , size, EVBUFFER_DATA(buffer)); |
626 | evbuffer_free(buffer); |
627 | } |
628 | |
629 | /* Callback for window_active_clients. */ |
630 | static void |
631 | format_cb_window_active_clients(struct format_tree *ft, struct format_entry *fe) |
632 | { |
633 | struct window *w = ft->wl->window; |
634 | struct client *loop; |
635 | struct session *client_session; |
636 | u_int n = 0; |
637 | |
638 | TAILQ_FOREACH(loop, &clients, entry) { |
639 | client_session = loop->session; |
640 | if (client_session == NULL) |
641 | continue; |
642 | |
643 | if (w == client_session->curw->window) |
644 | n++; |
645 | } |
646 | |
647 | xasprintf(&fe->value, "%u" , n); |
648 | } |
649 | |
650 | /* Callback for window_active_clients_list. */ |
651 | static void |
652 | format_cb_window_active_clients_list(struct format_tree *ft, |
653 | struct format_entry *fe) |
654 | { |
655 | struct window *w = ft->wl->window; |
656 | struct client *loop; |
657 | struct session *client_session; |
658 | struct evbuffer *buffer; |
659 | int size; |
660 | |
661 | buffer = evbuffer_new(); |
662 | if (buffer == NULL) |
663 | fatalx("out of memory" ); |
664 | |
665 | TAILQ_FOREACH(loop, &clients, entry) { |
666 | client_session = loop->session; |
667 | if (client_session == NULL) |
668 | continue; |
669 | |
670 | if (w == client_session->curw->window) { |
671 | if (EVBUFFER_LENGTH(buffer) > 0) |
672 | evbuffer_add(buffer, "," , 1); |
673 | evbuffer_add_printf(buffer, "%s" , loop->name); |
674 | } |
675 | } |
676 | |
677 | if ((size = EVBUFFER_LENGTH(buffer)) != 0) |
678 | xasprintf(&fe->value, "%.*s" , size, EVBUFFER_DATA(buffer)); |
679 | evbuffer_free(buffer); |
680 | } |
681 | |
682 | /* Callback for window_layout. */ |
683 | static void |
684 | format_cb_window_layout(struct format_tree *ft, struct format_entry *fe) |
685 | { |
686 | struct window *w = ft->w; |
687 | |
688 | if (w == NULL) |
689 | return; |
690 | |
691 | if (w->saved_layout_root != NULL) |
692 | fe->value = layout_dump(w->saved_layout_root); |
693 | else |
694 | fe->value = layout_dump(w->layout_root); |
695 | } |
696 | |
697 | /* Callback for window_visible_layout. */ |
698 | static void |
699 | format_cb_window_visible_layout(struct format_tree *ft, struct format_entry *fe) |
700 | { |
701 | struct window *w = ft->w; |
702 | |
703 | if (w == NULL) |
704 | return; |
705 | |
706 | fe->value = layout_dump(w->layout_root); |
707 | } |
708 | |
709 | /* Callback for pane_start_command. */ |
710 | static void |
711 | format_cb_start_command(struct format_tree *ft, struct format_entry *fe) |
712 | { |
713 | struct window_pane *wp = ft->wp; |
714 | |
715 | if (wp == NULL) |
716 | return; |
717 | |
718 | fe->value = cmd_stringify_argv(wp->argc, wp->argv); |
719 | } |
720 | |
721 | /* Callback for pane_current_command. */ |
722 | static void |
723 | format_cb_current_command(struct format_tree *ft, struct format_entry *fe) |
724 | { |
725 | struct window_pane *wp = ft->wp; |
726 | char *cmd; |
727 | |
728 | if (wp == NULL || wp->shell == NULL) |
729 | return; |
730 | |
731 | cmd = osdep_get_name(wp->fd, wp->tty); |
732 | if (cmd == NULL || *cmd == '\0') { |
733 | free(cmd); |
734 | cmd = cmd_stringify_argv(wp->argc, wp->argv); |
735 | if (cmd == NULL || *cmd == '\0') { |
736 | free(cmd); |
737 | cmd = xstrdup(wp->shell); |
738 | } |
739 | } |
740 | fe->value = parse_window_name(cmd); |
741 | free(cmd); |
742 | } |
743 | |
744 | /* Callback for pane_current_path. */ |
745 | static void |
746 | format_cb_current_path(struct format_tree *ft, struct format_entry *fe) |
747 | { |
748 | struct window_pane *wp = ft->wp; |
749 | char *cwd; |
750 | |
751 | if (wp == NULL) |
752 | return; |
753 | |
754 | cwd = osdep_get_cwd(wp->fd); |
755 | if (cwd != NULL) |
756 | fe->value = xstrdup(cwd); |
757 | } |
758 | |
759 | /* Callback for history_bytes. */ |
760 | static void |
761 | format_cb_history_bytes(struct format_tree *ft, struct format_entry *fe) |
762 | { |
763 | struct window_pane *wp = ft->wp; |
764 | struct grid *gd; |
765 | struct grid_line *gl; |
766 | unsigned long long size; |
767 | u_int i; |
768 | |
769 | if (wp == NULL) |
770 | return; |
771 | gd = wp->base.grid; |
772 | |
773 | size = 0; |
774 | for (i = 0; i < gd->hsize; i++) { |
775 | gl = grid_get_line(gd, i); |
776 | size += gl->cellsize * sizeof *gl->celldata; |
777 | size += gl->extdsize * sizeof *gl->extddata; |
778 | } |
779 | size += gd->hsize * sizeof *gl; |
780 | |
781 | xasprintf(&fe->value, "%llu" , size); |
782 | } |
783 | |
784 | /* Callback for pane_tabs. */ |
785 | static void |
786 | format_cb_pane_tabs(struct format_tree *ft, struct format_entry *fe) |
787 | { |
788 | struct window_pane *wp = ft->wp; |
789 | struct evbuffer *buffer; |
790 | u_int i; |
791 | int size; |
792 | |
793 | if (wp == NULL) |
794 | return; |
795 | |
796 | buffer = evbuffer_new(); |
797 | if (buffer == NULL) |
798 | fatalx("out of memory" ); |
799 | for (i = 0; i < wp->base.grid->sx; i++) { |
800 | if (!bit_test(wp->base.tabs, i)) |
801 | continue; |
802 | |
803 | if (EVBUFFER_LENGTH(buffer) > 0) |
804 | evbuffer_add(buffer, "," , 1); |
805 | evbuffer_add_printf(buffer, "%u" , i); |
806 | } |
807 | if ((size = EVBUFFER_LENGTH(buffer)) != 0) |
808 | xasprintf(&fe->value, "%.*s" , size, EVBUFFER_DATA(buffer)); |
809 | evbuffer_free(buffer); |
810 | } |
811 | |
812 | /* Callback for session_group_list. */ |
813 | static void |
814 | format_cb_session_group_list(struct format_tree *ft, struct format_entry *fe) |
815 | { |
816 | struct session *s = ft->s; |
817 | struct session_group *sg; |
818 | struct session *loop; |
819 | struct evbuffer *buffer; |
820 | int size; |
821 | |
822 | if (s == NULL) |
823 | return; |
824 | sg = session_group_contains(s); |
825 | if (sg == NULL) |
826 | return; |
827 | |
828 | buffer = evbuffer_new(); |
829 | if (buffer == NULL) |
830 | fatalx("out of memory" ); |
831 | |
832 | TAILQ_FOREACH(loop, &sg->sessions, gentry) { |
833 | if (EVBUFFER_LENGTH(buffer) > 0) |
834 | evbuffer_add(buffer, "," , 1); |
835 | evbuffer_add_printf(buffer, "%s" , loop->name); |
836 | } |
837 | |
838 | if ((size = EVBUFFER_LENGTH(buffer)) != 0) |
839 | xasprintf(&fe->value, "%.*s" , size, EVBUFFER_DATA(buffer)); |
840 | evbuffer_free(buffer); |
841 | } |
842 | |
843 | /* Callback for session_group_attached_list. */ |
844 | static void |
845 | format_cb_session_group_attached_list(struct format_tree *ft, |
846 | struct format_entry *fe) |
847 | { |
848 | struct session *s = ft->s, *client_session, *session_loop; |
849 | struct session_group *sg; |
850 | struct client *loop; |
851 | struct evbuffer *buffer; |
852 | int size; |
853 | |
854 | if (s == NULL) |
855 | return; |
856 | sg = session_group_contains(s); |
857 | if (sg == NULL) |
858 | return; |
859 | |
860 | buffer = evbuffer_new(); |
861 | if (buffer == NULL) |
862 | fatalx("out of memory" ); |
863 | |
864 | TAILQ_FOREACH(loop, &clients, entry) { |
865 | client_session = loop->session; |
866 | if (client_session == NULL) |
867 | continue; |
868 | TAILQ_FOREACH(session_loop, &sg->sessions, gentry) { |
869 | if (session_loop == client_session){ |
870 | if (EVBUFFER_LENGTH(buffer) > 0) |
871 | evbuffer_add(buffer, "," , 1); |
872 | evbuffer_add_printf(buffer, "%s" , loop->name); |
873 | } |
874 | } |
875 | } |
876 | |
877 | if ((size = EVBUFFER_LENGTH(buffer)) != 0) |
878 | xasprintf(&fe->value, "%.*s" , size, EVBUFFER_DATA(buffer)); |
879 | evbuffer_free(buffer); |
880 | } |
881 | |
882 | /* Callback for pane_in_mode. */ |
883 | static void |
884 | format_cb_pane_in_mode(struct format_tree *ft, struct format_entry *fe) |
885 | { |
886 | struct window_pane *wp = ft->wp; |
887 | u_int n = 0; |
888 | struct window_mode_entry *wme; |
889 | |
890 | if (wp == NULL) |
891 | return; |
892 | |
893 | TAILQ_FOREACH(wme, &wp->modes, entry) |
894 | n++; |
895 | xasprintf(&fe->value, "%u" , n); |
896 | } |
897 | |
898 | /* Callback for pane_at_top. */ |
899 | static void |
900 | format_cb_pane_at_top(struct format_tree *ft, struct format_entry *fe) |
901 | { |
902 | struct window_pane *wp = ft->wp; |
903 | struct window *w = wp->window; |
904 | int status, flag; |
905 | |
906 | if (wp == NULL) |
907 | return; |
908 | |
909 | status = options_get_number(w->options, "pane-border-status" ); |
910 | if (status == PANE_STATUS_TOP) |
911 | flag = (wp->yoff == 1); |
912 | else |
913 | flag = (wp->yoff == 0); |
914 | xasprintf(&fe->value, "%d" , flag); |
915 | } |
916 | |
917 | /* Callback for pane_at_bottom. */ |
918 | static void |
919 | format_cb_pane_at_bottom(struct format_tree *ft, struct format_entry *fe) |
920 | { |
921 | struct window_pane *wp = ft->wp; |
922 | struct window *w = wp->window; |
923 | int status, flag; |
924 | |
925 | if (wp == NULL) |
926 | return; |
927 | |
928 | status = options_get_number(w->options, "pane-border-status" ); |
929 | if (status == PANE_STATUS_BOTTOM) |
930 | flag = (wp->yoff + wp->sy == w->sy - 1); |
931 | else |
932 | flag = (wp->yoff + wp->sy == w->sy); |
933 | xasprintf(&fe->value, "%d" , flag); |
934 | } |
935 | |
936 | /* Callback for cursor_character. */ |
937 | static void |
938 | format_cb_cursor_character(struct format_tree *ft, struct format_entry *fe) |
939 | { |
940 | struct window_pane *wp = ft->wp; |
941 | struct grid_cell gc; |
942 | |
943 | if (wp == NULL) |
944 | return; |
945 | |
946 | grid_view_get_cell(wp->base.grid, wp->base.cx, wp->base.cy, &gc); |
947 | if (~gc.flags & GRID_FLAG_PADDING) |
948 | xasprintf(&fe->value, "%.*s" , (int)gc.data.size, gc.data.data); |
949 | } |
950 | |
951 | /* Return word at given coordinates. Caller frees. */ |
952 | char * |
953 | format_grid_word(struct grid *gd, u_int x, u_int y) |
954 | { |
955 | struct grid_line *gl; |
956 | struct grid_cell gc; |
957 | const char *ws; |
958 | struct utf8_data *ud = NULL; |
959 | u_int end; |
960 | size_t size = 0; |
961 | int found = 0; |
962 | char *s = NULL; |
963 | |
964 | ws = options_get_string(global_s_options, "word-separators" ); |
965 | |
966 | y = gd->hsize + y; |
967 | for (;;) { |
968 | grid_get_cell(gd, x, y, &gc); |
969 | if (gc.flags & GRID_FLAG_PADDING) |
970 | break; |
971 | if (utf8_cstrhas(ws, &gc.data)) { |
972 | found = 1; |
973 | break; |
974 | } |
975 | |
976 | if (x == 0) { |
977 | if (y == 0) |
978 | break; |
979 | gl = &gd->linedata[y - 1]; |
980 | if (~gl->flags & GRID_LINE_WRAPPED) |
981 | break; |
982 | y--; |
983 | x = grid_line_length(gd, y); |
984 | if (x == 0) |
985 | break; |
986 | } |
987 | x--; |
988 | } |
989 | for (;;) { |
990 | if (found) { |
991 | end = grid_line_length(gd, y); |
992 | if (end == 0 || x == end - 1) { |
993 | if (y == gd->hsize + gd->sy - 1) |
994 | break; |
995 | gl = &gd->linedata[y]; |
996 | if (~gl->flags & GRID_LINE_WRAPPED) |
997 | break; |
998 | y++; |
999 | x = 0; |
1000 | } else |
1001 | x++; |
1002 | } |
1003 | found = 1; |
1004 | |
1005 | grid_get_cell(gd, x, y, &gc); |
1006 | if (gc.flags & GRID_FLAG_PADDING) |
1007 | break; |
1008 | if (utf8_cstrhas(ws, &gc.data)) |
1009 | break; |
1010 | |
1011 | ud = xreallocarray(ud, size + 2, sizeof *ud); |
1012 | memcpy(&ud[size++], &gc.data, sizeof *ud); |
1013 | } |
1014 | if (size != 0) { |
1015 | ud[size].size = 0; |
1016 | s = utf8_tocstr(ud); |
1017 | free(ud); |
1018 | } |
1019 | return (s); |
1020 | } |
1021 | |
1022 | /* Callback for mouse_word. */ |
1023 | static void |
1024 | format_cb_mouse_word(struct format_tree *ft, struct format_entry *fe) |
1025 | { |
1026 | struct window_pane *wp; |
1027 | u_int x, y; |
1028 | char *s; |
1029 | |
1030 | if (!ft->m.valid) |
1031 | return; |
1032 | wp = cmd_mouse_pane(&ft->m, NULL, NULL); |
1033 | if (wp == NULL) |
1034 | return; |
1035 | if (!TAILQ_EMPTY (&wp->modes)) |
1036 | return; |
1037 | if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0) |
1038 | return; |
1039 | |
1040 | s = format_grid_word(wp->base.grid, x, y); |
1041 | if (s != NULL) |
1042 | fe->value = s; |
1043 | } |
1044 | |
1045 | /* Return line at given coordinates. Caller frees. */ |
1046 | char * |
1047 | format_grid_line(struct grid *gd, u_int y) |
1048 | { |
1049 | struct grid_cell gc; |
1050 | struct utf8_data *ud = NULL; |
1051 | u_int x; |
1052 | size_t size = 0; |
1053 | char *s = NULL; |
1054 | |
1055 | y = gd->hsize + y; |
1056 | for (x = 0; x < grid_line_length(gd, y); x++) { |
1057 | grid_get_cell(gd, x, y, &gc); |
1058 | if (gc.flags & GRID_FLAG_PADDING) |
1059 | break; |
1060 | |
1061 | ud = xreallocarray(ud, size + 2, sizeof *ud); |
1062 | memcpy(&ud[size++], &gc.data, sizeof *ud); |
1063 | } |
1064 | if (size != 0) { |
1065 | ud[size].size = 0; |
1066 | s = utf8_tocstr(ud); |
1067 | free(ud); |
1068 | } |
1069 | return (s); |
1070 | } |
1071 | |
1072 | /* Callback for mouse_line. */ |
1073 | static void |
1074 | format_cb_mouse_line(struct format_tree *ft, struct format_entry *fe) |
1075 | { |
1076 | struct window_pane *wp; |
1077 | u_int x, y; |
1078 | char *s; |
1079 | |
1080 | if (!ft->m.valid) |
1081 | return; |
1082 | wp = cmd_mouse_pane(&ft->m, NULL, NULL); |
1083 | if (wp == NULL) |
1084 | return; |
1085 | if (!TAILQ_EMPTY (&wp->modes)) |
1086 | return; |
1087 | if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0) |
1088 | return; |
1089 | |
1090 | s = format_grid_line(wp->base.grid, y); |
1091 | if (s != NULL) |
1092 | fe->value = s; |
1093 | } |
1094 | |
1095 | /* Merge a format tree. */ |
1096 | static void |
1097 | format_merge(struct format_tree *ft, struct format_tree *from) |
1098 | { |
1099 | struct format_entry *fe; |
1100 | |
1101 | RB_FOREACH(fe, format_entry_tree, &from->tree) { |
1102 | if (fe->value != NULL) |
1103 | format_add(ft, fe->key, "%s" , fe->value); |
1104 | } |
1105 | } |
1106 | |
1107 | /* Add item bits to tree. */ |
1108 | static void |
1109 | format_create_add_item(struct format_tree *ft, struct cmdq_item *item) |
1110 | { |
1111 | struct mouse_event *m; |
1112 | struct window_pane *wp; |
1113 | u_int x, y; |
1114 | |
1115 | if (item->cmd != NULL) |
1116 | format_add(ft, "command" , "%s" , item->cmd->entry->name); |
1117 | |
1118 | if (item->shared == NULL) |
1119 | return; |
1120 | if (item->shared->formats != NULL) |
1121 | format_merge(ft, item->shared->formats); |
1122 | |
1123 | m = &item->shared->mouse; |
1124 | if (m->valid && ((wp = cmd_mouse_pane(m, NULL, NULL)) != NULL)) { |
1125 | format_add(ft, "mouse_pane" , "%%%u" , wp->id); |
1126 | if (cmd_mouse_at(wp, m, &x, &y, 0) == 0) { |
1127 | format_add(ft, "mouse_x" , "%u" , x); |
1128 | format_add(ft, "mouse_y" , "%u" , y); |
1129 | format_add_cb(ft, "mouse_word" , format_cb_mouse_word); |
1130 | format_add_cb(ft, "mouse_line" , format_cb_mouse_line); |
1131 | } |
1132 | } |
1133 | memcpy(&ft->m, m, sizeof ft->m); |
1134 | } |
1135 | |
1136 | /* Create a new tree. */ |
1137 | struct format_tree * |
1138 | format_create(struct client *c, struct cmdq_item *item, int tag, int flags) |
1139 | { |
1140 | struct format_tree *ft; |
1141 | const struct window_mode **wm; |
1142 | char tmp[64]; |
1143 | |
1144 | if (!event_initialized(&format_job_event)) { |
1145 | evtimer_set(&format_job_event, format_job_timer, NULL); |
1146 | format_job_timer(-1, 0, NULL); |
1147 | } |
1148 | |
1149 | ft = xcalloc(1, sizeof *ft); |
1150 | RB_INIT(&ft->tree); |
1151 | |
1152 | if (c != NULL) { |
1153 | ft->client = c; |
1154 | ft->client->references++; |
1155 | } |
1156 | ft->item = item; |
1157 | |
1158 | ft->tag = tag; |
1159 | ft->flags = flags; |
1160 | ft->time = time(NULL); |
1161 | |
1162 | format_add(ft, "version" , "%s" , getversion()); |
1163 | format_add_cb(ft, "host" , format_cb_host); |
1164 | format_add_cb(ft, "host_short" , format_cb_host_short); |
1165 | format_add_cb(ft, "pid" , format_cb_pid); |
1166 | format_add(ft, "socket_path" , "%s" , socket_path); |
1167 | format_add_tv(ft, "start_time" , &start_time); |
1168 | |
1169 | for (wm = all_window_modes; *wm != NULL; wm++) { |
1170 | if ((*wm)->default_format != NULL) { |
1171 | xsnprintf(tmp, sizeof tmp, "%s_format" , (*wm)->name); |
1172 | tmp[strcspn(tmp, "-" )] = '_'; |
1173 | format_add(ft, tmp, "%s" , (*wm)->default_format); |
1174 | } |
1175 | } |
1176 | |
1177 | if (item != NULL) |
1178 | format_create_add_item(ft, item); |
1179 | |
1180 | return (ft); |
1181 | } |
1182 | |
1183 | /* Free a tree. */ |
1184 | void |
1185 | format_free(struct format_tree *ft) |
1186 | { |
1187 | struct format_entry *fe, *fe1; |
1188 | |
1189 | RB_FOREACH_SAFE(fe, format_entry_tree, &ft->tree, fe1) { |
1190 | RB_REMOVE(format_entry_tree, &ft->tree, fe); |
1191 | free(fe->value); |
1192 | free(fe->key); |
1193 | free(fe); |
1194 | } |
1195 | |
1196 | if (ft->client != NULL) |
1197 | server_client_unref(ft->client); |
1198 | free(ft); |
1199 | } |
1200 | |
1201 | /* Walk each format. */ |
1202 | void |
1203 | format_each(struct format_tree *ft, void (*cb)(const char *, const char *, |
1204 | void *), void *arg) |
1205 | { |
1206 | struct format_entry *fe; |
1207 | char s[64]; |
1208 | |
1209 | RB_FOREACH(fe, format_entry_tree, &ft->tree) { |
1210 | if (fe->t != 0) { |
1211 | xsnprintf(s, sizeof s, "%lld" , (long long)fe->t); |
1212 | cb(fe->key, s, arg); |
1213 | } else { |
1214 | if (fe->value == NULL && fe->cb != NULL) { |
1215 | fe->cb(ft, fe); |
1216 | if (fe->value == NULL) |
1217 | fe->value = xstrdup("" ); |
1218 | } |
1219 | cb(fe->key, fe->value, arg); |
1220 | } |
1221 | } |
1222 | } |
1223 | |
1224 | /* Add a key-value pair. */ |
1225 | void |
1226 | format_add(struct format_tree *ft, const char *key, const char *fmt, ...) |
1227 | { |
1228 | struct format_entry *fe; |
1229 | struct format_entry *fe_now; |
1230 | va_list ap; |
1231 | |
1232 | fe = xmalloc(sizeof *fe); |
1233 | fe->key = xstrdup(key); |
1234 | |
1235 | fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe); |
1236 | if (fe_now != NULL) { |
1237 | free(fe->key); |
1238 | free(fe); |
1239 | free(fe_now->value); |
1240 | fe = fe_now; |
1241 | } |
1242 | |
1243 | fe->cb = NULL; |
1244 | fe->t = 0; |
1245 | |
1246 | va_start(ap, fmt); |
1247 | xvasprintf(&fe->value, fmt, ap); |
1248 | va_end(ap); |
1249 | } |
1250 | |
1251 | /* Add a key and time. */ |
1252 | static void |
1253 | format_add_tv(struct format_tree *ft, const char *key, struct timeval *tv) |
1254 | { |
1255 | struct format_entry *fe, *fe_now; |
1256 | |
1257 | fe = xmalloc(sizeof *fe); |
1258 | fe->key = xstrdup(key); |
1259 | |
1260 | fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe); |
1261 | if (fe_now != NULL) { |
1262 | free(fe->key); |
1263 | free(fe); |
1264 | free(fe_now->value); |
1265 | fe = fe_now; |
1266 | } |
1267 | |
1268 | fe->cb = NULL; |
1269 | fe->t = tv->tv_sec; |
1270 | |
1271 | fe->value = NULL; |
1272 | } |
1273 | |
1274 | /* Add a key and function. */ |
1275 | static void |
1276 | format_add_cb(struct format_tree *ft, const char *key, format_cb cb) |
1277 | { |
1278 | struct format_entry *fe; |
1279 | struct format_entry *fe_now; |
1280 | |
1281 | fe = xmalloc(sizeof *fe); |
1282 | fe->key = xstrdup(key); |
1283 | |
1284 | fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe); |
1285 | if (fe_now != NULL) { |
1286 | free(fe->key); |
1287 | free(fe); |
1288 | free(fe_now->value); |
1289 | fe = fe_now; |
1290 | } |
1291 | |
1292 | fe->cb = cb; |
1293 | fe->t = 0; |
1294 | |
1295 | fe->value = NULL; |
1296 | } |
1297 | |
1298 | /* Quote special characters in string. */ |
1299 | static char * |
1300 | format_quote(const char *s) |
1301 | { |
1302 | const char *cp; |
1303 | char *out, *at; |
1304 | |
1305 | at = out = xmalloc(strlen(s) * 2 + 1); |
1306 | for (cp = s; *cp != '\0'; cp++) { |
1307 | if (strchr("|&;<>()$`\\\"'*?[# =%" , *cp) != NULL) |
1308 | *at++ = '\\'; |
1309 | *at++ = *cp; |
1310 | } |
1311 | *at = '\0'; |
1312 | return (out); |
1313 | } |
1314 | |
1315 | /* Find a format entry. */ |
1316 | static char * |
1317 | format_find(struct format_tree *ft, const char *key, int modifiers) |
1318 | { |
1319 | struct format_entry *fe, fe_find; |
1320 | struct environ_entry *envent; |
1321 | static char s[64]; |
1322 | struct options_entry *o; |
1323 | int idx; |
1324 | char *found, *saved; |
1325 | |
1326 | if (~modifiers & FORMAT_TIMESTRING) { |
1327 | o = options_parse_get(global_options, key, &idx, 0); |
1328 | if (o == NULL && ft->wp != NULL) |
1329 | o = options_parse_get(ft->wp->options, key, &idx, 0); |
1330 | if (o == NULL && ft->w != NULL) |
1331 | o = options_parse_get(ft->w->options, key, &idx, 0); |
1332 | if (o == NULL) |
1333 | o = options_parse_get(global_w_options, key, &idx, 0); |
1334 | if (o == NULL && ft->s != NULL) |
1335 | o = options_parse_get(ft->s->options, key, &idx, 0); |
1336 | if (o == NULL) |
1337 | o = options_parse_get(global_s_options, key, &idx, 0); |
1338 | if (o != NULL) { |
1339 | found = options_tostring(o, idx, 1); |
1340 | goto found; |
1341 | } |
1342 | } |
1343 | found = NULL; |
1344 | |
1345 | fe_find.key = (char *) key; |
1346 | fe = RB_FIND(format_entry_tree, &ft->tree, &fe_find); |
1347 | if (fe != NULL) { |
1348 | if (modifiers & FORMAT_TIMESTRING) { |
1349 | if (fe->t == 0) |
1350 | return (NULL); |
1351 | ctime_r(&fe->t, s); |
1352 | s[strcspn(s, "\n" )] = '\0'; |
1353 | found = xstrdup(s); |
1354 | goto found; |
1355 | } |
1356 | if (fe->t != 0) { |
1357 | xasprintf(&found, "%lld" , (long long)fe->t); |
1358 | goto found; |
1359 | } |
1360 | if (fe->value == NULL && fe->cb != NULL) |
1361 | fe->cb(ft, fe); |
1362 | if (fe->value == NULL) |
1363 | fe->value = xstrdup("" ); |
1364 | found = xstrdup(fe->value); |
1365 | goto found; |
1366 | } |
1367 | |
1368 | if (~modifiers & FORMAT_TIMESTRING) { |
1369 | envent = NULL; |
1370 | if (ft->s != NULL) |
1371 | envent = environ_find(ft->s->environ, key); |
1372 | if (envent == NULL) |
1373 | envent = environ_find(global_environ, key); |
1374 | if (envent != NULL && envent->value != NULL) { |
1375 | found = xstrdup(envent->value); |
1376 | goto found; |
1377 | } |
1378 | } |
1379 | |
1380 | return (NULL); |
1381 | |
1382 | found: |
1383 | if (found == NULL) |
1384 | return (NULL); |
1385 | if (modifiers & FORMAT_BASENAME) { |
1386 | saved = found; |
1387 | found = xstrdup(basename(saved)); |
1388 | free(saved); |
1389 | } |
1390 | if (modifiers & FORMAT_DIRNAME) { |
1391 | saved = found; |
1392 | found = xstrdup(dirname(saved)); |
1393 | free(saved); |
1394 | } |
1395 | if (modifiers & FORMAT_QUOTE) { |
1396 | saved = found; |
1397 | found = xstrdup(format_quote(saved)); |
1398 | free(saved); |
1399 | } |
1400 | return (found); |
1401 | } |
1402 | |
1403 | /* Skip until end. */ |
1404 | const char * |
1405 | format_skip(const char *s, const char *end) |
1406 | { |
1407 | int brackets = 0; |
1408 | |
1409 | for (; *s != '\0'; s++) { |
1410 | if (*s == '#' && s[1] == '{') |
1411 | brackets++; |
1412 | if (*s == '#' && strchr(",#{}" , s[1]) != NULL) { |
1413 | s++; |
1414 | continue; |
1415 | } |
1416 | if (*s == '}') |
1417 | brackets--; |
1418 | if (strchr(end, *s) != NULL && brackets == 0) |
1419 | break; |
1420 | } |
1421 | if (*s == '\0') |
1422 | return (NULL); |
1423 | return (s); |
1424 | } |
1425 | |
1426 | /* Return left and right alternatives separated by commas. */ |
1427 | static int |
1428 | format_choose(struct format_tree *ft, const char *s, char **left, char **right, |
1429 | int expand) |
1430 | { |
1431 | const char *cp; |
1432 | char *left0, *right0; |
1433 | |
1434 | cp = format_skip(s, "," ); |
1435 | if (cp == NULL) |
1436 | return (-1); |
1437 | left0 = xstrndup(s, cp - s); |
1438 | right0 = xstrdup(cp + 1); |
1439 | |
1440 | if (expand) { |
1441 | *left = format_expand(ft, left0); |
1442 | free(left0); |
1443 | *right = format_expand(ft, right0); |
1444 | free(right0); |
1445 | } else { |
1446 | *left = left0; |
1447 | *right = right0; |
1448 | } |
1449 | return (0); |
1450 | } |
1451 | |
1452 | /* Is this true? */ |
1453 | int |
1454 | format_true(const char *s) |
1455 | { |
1456 | if (s != NULL && *s != '\0' && (s[0] != '0' || s[1] != '\0')) |
1457 | return (1); |
1458 | return (0); |
1459 | } |
1460 | |
1461 | /* Check if modifier end. */ |
1462 | static int |
1463 | format_is_end(char c) |
1464 | { |
1465 | return (c == ';' || c == ':'); |
1466 | } |
1467 | |
1468 | /* Add to modifier list. */ |
1469 | static void |
1470 | format_add_modifier(struct format_modifier **list, u_int *count, |
1471 | const char *c, size_t n, char **argv, int argc) |
1472 | { |
1473 | struct format_modifier *fm; |
1474 | |
1475 | *list = xreallocarray(*list, (*count) + 1, sizeof **list); |
1476 | fm = &(*list)[(*count)++]; |
1477 | |
1478 | memcpy(fm->modifier, c, n); |
1479 | fm->modifier[n] = '\0'; |
1480 | fm->size = n; |
1481 | |
1482 | fm->argv = argv; |
1483 | fm->argc = argc; |
1484 | } |
1485 | |
1486 | /* Free modifier list. */ |
1487 | static void |
1488 | format_free_modifiers(struct format_modifier *list, u_int count) |
1489 | { |
1490 | u_int i; |
1491 | |
1492 | for (i = 0; i < count; i++) |
1493 | cmd_free_argv(list[i].argc, list[i].argv); |
1494 | free(list); |
1495 | } |
1496 | |
1497 | /* Build modifier list. */ |
1498 | static struct format_modifier * |
1499 | format_build_modifiers(struct format_tree *ft, const char **s, u_int *count) |
1500 | { |
1501 | const char *cp = *s, *end; |
1502 | struct format_modifier *list = NULL; |
1503 | char c, last[] = "X;:" , **argv, *value; |
1504 | int argc; |
1505 | |
1506 | /* |
1507 | * Modifiers are a ; separated list of the forms: |
1508 | * l,m,C,b,d,t,q,E,T,S,W,P,<,> |
1509 | * =a |
1510 | * =/a |
1511 | * =/a/ |
1512 | * s/a/b/ |
1513 | * s/a/b |
1514 | * ||,&&,!=,==,<=,>= |
1515 | */ |
1516 | |
1517 | *count = 0; |
1518 | |
1519 | while (*cp != '\0' && *cp != ':') { |
1520 | /* Skip and separator character. */ |
1521 | if (*cp == ';') |
1522 | cp++; |
1523 | |
1524 | /* Check single character modifiers with no arguments. */ |
1525 | if (strchr("lbdtqETSWP<>" , cp[0]) != NULL && |
1526 | format_is_end(cp[1])) { |
1527 | format_add_modifier(&list, count, cp, 1, NULL, 0); |
1528 | cp++; |
1529 | continue; |
1530 | } |
1531 | |
1532 | /* Then try double character with no arguments. */ |
1533 | if ((memcmp("||" , cp, 2) == 0 || |
1534 | memcmp("&&" , cp, 2) == 0 || |
1535 | memcmp("!=" , cp, 2) == 0 || |
1536 | memcmp("==" , cp, 2) == 0 || |
1537 | memcmp("<=" , cp, 2) == 0 || |
1538 | memcmp(">=" , cp, 2) == 0) && |
1539 | format_is_end(cp[2])) { |
1540 | format_add_modifier(&list, count, cp, 2, NULL, 0); |
1541 | cp += 2; |
1542 | continue; |
1543 | } |
1544 | |
1545 | /* Now try single character with arguments. */ |
1546 | if (strchr("mCs=p" , cp[0]) == NULL) |
1547 | break; |
1548 | c = cp[0]; |
1549 | |
1550 | /* No arguments provided. */ |
1551 | if (format_is_end(cp[1])) { |
1552 | format_add_modifier(&list, count, cp, 1, NULL, 0); |
1553 | cp++; |
1554 | continue; |
1555 | } |
1556 | argv = NULL; |
1557 | argc = 0; |
1558 | |
1559 | /* Single argument with no wrapper character. */ |
1560 | if (!ispunct(cp[1]) || cp[1] == '-') { |
1561 | end = format_skip(cp + 1, ":;" ); |
1562 | if (end == NULL) |
1563 | break; |
1564 | |
1565 | argv = xcalloc(1, sizeof *argv); |
1566 | value = xstrndup(cp + 1, end - (cp + 1)); |
1567 | argv[0] = format_expand(ft, value); |
1568 | free(value); |
1569 | argc = 1; |
1570 | |
1571 | format_add_modifier(&list, count, &c, 1, argv, argc); |
1572 | cp = end; |
1573 | continue; |
1574 | } |
1575 | |
1576 | /* Multiple arguments with a wrapper character. */ |
1577 | last[0] = cp[1]; |
1578 | cp++; |
1579 | do { |
1580 | if (cp[0] == last[0] && format_is_end(cp[1])) { |
1581 | cp++; |
1582 | break; |
1583 | } |
1584 | end = format_skip(cp + 1, last); |
1585 | if (end == NULL) |
1586 | break; |
1587 | cp++; |
1588 | |
1589 | argv = xreallocarray (argv, argc + 1, sizeof *argv); |
1590 | value = xstrndup(cp, end - cp); |
1591 | argv[argc++] = format_expand(ft, value); |
1592 | free(value); |
1593 | |
1594 | cp = end; |
1595 | } while (!format_is_end(cp[0])); |
1596 | format_add_modifier(&list, count, &c, 1, argv, argc); |
1597 | } |
1598 | if (*cp != ':') { |
1599 | format_free_modifiers(list, *count); |
1600 | *count = 0; |
1601 | return (NULL); |
1602 | } |
1603 | *s = cp + 1; |
1604 | return list; |
1605 | } |
1606 | |
1607 | /* Match against an fnmatch(3) pattern or regular expression. */ |
1608 | static char * |
1609 | format_match(struct format_modifier *fm, const char *pattern, const char *text) |
1610 | { |
1611 | const char *s = "" ; |
1612 | regex_t r; |
1613 | int flags = 0; |
1614 | |
1615 | if (fm->argc >= 1) |
1616 | s = fm->argv[0]; |
1617 | if (strchr(s, 'r') == NULL) { |
1618 | if (strchr(s, 'i') != NULL) |
1619 | flags |= FNM_CASEFOLD; |
1620 | if (fnmatch(pattern, text, flags) != 0) |
1621 | return (xstrdup("0" )); |
1622 | } else { |
1623 | flags = REG_EXTENDED|REG_NOSUB; |
1624 | if (strchr(s, 'i') != NULL) |
1625 | flags |= REG_ICASE; |
1626 | if (regcomp(&r, pattern, flags) != 0) |
1627 | return (xstrdup("0" )); |
1628 | if (regexec(&r, text, 0, NULL, 0) != 0) { |
1629 | regfree(&r); |
1630 | return (xstrdup("0" )); |
1631 | } |
1632 | regfree(&r); |
1633 | } |
1634 | return (xstrdup("1" )); |
1635 | } |
1636 | |
1637 | /* Perform substitution in string. */ |
1638 | static char * |
1639 | format_sub(struct format_modifier *fm, const char *text, const char *pattern, |
1640 | const char *with) |
1641 | { |
1642 | char *value; |
1643 | int flags = REG_EXTENDED; |
1644 | |
1645 | if (fm->argc >= 3 && strchr(fm->argv[2], 'i') != NULL) |
1646 | flags |= REG_ICASE; |
1647 | value = regsub(pattern, with, text, flags); |
1648 | if (value == NULL) |
1649 | return (xstrdup(text)); |
1650 | return (value); |
1651 | } |
1652 | |
1653 | /* Search inside pane. */ |
1654 | static char * |
1655 | format_search(struct format_modifier *fm, struct window_pane *wp, const char *s) |
1656 | { |
1657 | int ignore = 0, regex = 0; |
1658 | char *value; |
1659 | |
1660 | if (fm->argc >= 1) { |
1661 | if (strchr(fm->argv[0], 'i') != NULL) |
1662 | ignore = 1; |
1663 | if (strchr(fm->argv[0], 'r') != NULL) |
1664 | regex = 1; |
1665 | } |
1666 | xasprintf(&value, "%u" , window_pane_search(wp, s, regex, ignore)); |
1667 | return (value); |
1668 | } |
1669 | |
1670 | /* Loop over sessions. */ |
1671 | static char * |
1672 | format_loop_sessions(struct format_tree *ft, const char *fmt) |
1673 | { |
1674 | struct client *c = ft->client; |
1675 | struct cmdq_item *item = ft->item; |
1676 | struct format_tree *nft; |
1677 | char *expanded, *value; |
1678 | size_t valuelen; |
1679 | struct session *s; |
1680 | |
1681 | value = xcalloc(1, 1); |
1682 | valuelen = 1; |
1683 | |
1684 | RB_FOREACH(s, sessions, &sessions) { |
1685 | format_log(ft, "session loop: $%u" , s->id); |
1686 | nft = format_create(c, item, FORMAT_NONE, ft->flags); |
1687 | nft->loop = ft->loop; |
1688 | format_defaults(nft, ft->c, s, NULL, NULL); |
1689 | expanded = format_expand(nft, fmt); |
1690 | format_free(nft); |
1691 | |
1692 | valuelen += strlen(expanded); |
1693 | value = xrealloc(value, valuelen); |
1694 | |
1695 | strlcat(value, expanded, valuelen); |
1696 | free(expanded); |
1697 | } |
1698 | |
1699 | return (value); |
1700 | } |
1701 | |
1702 | /* Loop over windows. */ |
1703 | static char * |
1704 | format_loop_windows(struct format_tree *ft, const char *fmt) |
1705 | { |
1706 | struct client *c = ft->client; |
1707 | struct cmdq_item *item = ft->item; |
1708 | struct format_tree *nft; |
1709 | char *all, *active, *use, *expanded, *value; |
1710 | size_t valuelen; |
1711 | struct winlink *wl; |
1712 | struct window *w; |
1713 | |
1714 | if (ft->s == NULL) { |
1715 | format_log(ft, "window loop but no session" ); |
1716 | return (NULL); |
1717 | } |
1718 | |
1719 | if (format_choose(ft, fmt, &all, &active, 0) != 0) { |
1720 | all = xstrdup(fmt); |
1721 | active = NULL; |
1722 | } |
1723 | |
1724 | value = xcalloc(1, 1); |
1725 | valuelen = 1; |
1726 | |
1727 | RB_FOREACH(wl, winlinks, &ft->s->windows) { |
1728 | w = wl->window; |
1729 | format_log(ft, "window loop: %u @%u" , wl->idx, w->id); |
1730 | if (active != NULL && wl == ft->s->curw) |
1731 | use = active; |
1732 | else |
1733 | use = all; |
1734 | nft = format_create(c, item, FORMAT_WINDOW|w->id, ft->flags); |
1735 | nft->loop = ft->loop; |
1736 | format_defaults(nft, ft->c, ft->s, wl, NULL); |
1737 | expanded = format_expand(nft, use); |
1738 | format_free(nft); |
1739 | |
1740 | valuelen += strlen(expanded); |
1741 | value = xrealloc(value, valuelen); |
1742 | |
1743 | strlcat(value, expanded, valuelen); |
1744 | free(expanded); |
1745 | } |
1746 | |
1747 | free(active); |
1748 | free(all); |
1749 | |
1750 | return (value); |
1751 | } |
1752 | |
1753 | /* Loop over panes. */ |
1754 | static char * |
1755 | format_loop_panes(struct format_tree *ft, const char *fmt) |
1756 | { |
1757 | struct client *c = ft->client; |
1758 | struct cmdq_item *item = ft->item; |
1759 | struct format_tree *nft; |
1760 | char *all, *active, *use, *expanded, *value; |
1761 | size_t valuelen; |
1762 | struct window_pane *wp; |
1763 | |
1764 | if (ft->w == NULL) { |
1765 | format_log(ft, "pane loop but no window" ); |
1766 | return (NULL); |
1767 | } |
1768 | |
1769 | if (format_choose(ft, fmt, &all, &active, 0) != 0) { |
1770 | all = xstrdup(fmt); |
1771 | active = NULL; |
1772 | } |
1773 | |
1774 | value = xcalloc(1, 1); |
1775 | valuelen = 1; |
1776 | |
1777 | TAILQ_FOREACH(wp, &ft->w->panes, entry) { |
1778 | format_log(ft, "pane loop: %%%u" , wp->id); |
1779 | if (active != NULL && wp == ft->w->active) |
1780 | use = active; |
1781 | else |
1782 | use = all; |
1783 | nft = format_create(c, item, FORMAT_PANE|wp->id, ft->flags); |
1784 | nft->loop = ft->loop; |
1785 | format_defaults(nft, ft->c, ft->s, ft->wl, wp); |
1786 | expanded = format_expand(nft, use); |
1787 | format_free(nft); |
1788 | |
1789 | valuelen += strlen(expanded); |
1790 | value = xrealloc(value, valuelen); |
1791 | |
1792 | strlcat(value, expanded, valuelen); |
1793 | free(expanded); |
1794 | } |
1795 | |
1796 | free(active); |
1797 | free(all); |
1798 | |
1799 | return (value); |
1800 | } |
1801 | |
1802 | /* Replace a key. */ |
1803 | static int |
1804 | format_replace(struct format_tree *ft, const char *key, size_t keylen, |
1805 | char **buf, size_t *len, size_t *off) |
1806 | { |
1807 | struct window_pane *wp = ft->wp; |
1808 | const char *errptr, *copy, *cp, *marker = NULL; |
1809 | char *copy0, *condition, *found, *new; |
1810 | char *value, *left, *right; |
1811 | size_t valuelen; |
1812 | int modifiers = 0, limit = 0, width = 0, j; |
1813 | struct format_modifier *list, *fm, *cmp = NULL, *search = NULL; |
1814 | struct format_modifier **sub = NULL; |
1815 | u_int i, count, nsub = 0; |
1816 | |
1817 | /* Make a copy of the key. */ |
1818 | copy = copy0 = xstrndup(key, keylen); |
1819 | |
1820 | /* Process modifier list. */ |
1821 | list = format_build_modifiers(ft, ©, &count); |
1822 | for (i = 0; i < count; i++) { |
1823 | fm = &list[i]; |
1824 | if (format_logging(ft)) { |
1825 | format_log(ft, "modifier %u is %s" , i, fm->modifier); |
1826 | for (j = 0; j < fm->argc; j++) { |
1827 | format_log(ft, "modifier %u argument %d: %s" , i, |
1828 | j, fm->argv[j]); |
1829 | } |
1830 | } |
1831 | if (fm->size == 1) { |
1832 | switch (fm->modifier[0]) { |
1833 | case 'm': |
1834 | case '<': |
1835 | case '>': |
1836 | cmp = fm; |
1837 | break; |
1838 | case 'C': |
1839 | search = fm; |
1840 | break; |
1841 | case 's': |
1842 | if (fm->argc < 2) |
1843 | break; |
1844 | sub = xreallocarray (sub, nsub + 1, |
1845 | sizeof *sub); |
1846 | sub[nsub++] = fm; |
1847 | break; |
1848 | case '=': |
1849 | if (fm->argc < 1) |
1850 | break; |
1851 | limit = strtonum(fm->argv[0], INT_MIN, INT_MAX, |
1852 | &errptr); |
1853 | if (errptr != NULL) |
1854 | limit = 0; |
1855 | if (fm->argc >= 2 && fm->argv[1] != NULL) |
1856 | marker = fm->argv[1]; |
1857 | break; |
1858 | case 'p': |
1859 | if (fm->argc < 1) |
1860 | break; |
1861 | width = strtonum(fm->argv[0], INT_MIN, INT_MAX, |
1862 | &errptr); |
1863 | if (errptr != NULL) |
1864 | width = 0; |
1865 | break; |
1866 | case 'l': |
1867 | modifiers |= FORMAT_LITERAL; |
1868 | break; |
1869 | case 'b': |
1870 | modifiers |= FORMAT_BASENAME; |
1871 | break; |
1872 | case 'd': |
1873 | modifiers |= FORMAT_DIRNAME; |
1874 | break; |
1875 | case 't': |
1876 | modifiers |= FORMAT_TIMESTRING; |
1877 | break; |
1878 | case 'q': |
1879 | modifiers |= FORMAT_QUOTE; |
1880 | break; |
1881 | case 'E': |
1882 | modifiers |= FORMAT_EXPAND; |
1883 | break; |
1884 | case 'T': |
1885 | modifiers |= FORMAT_EXPANDTIME; |
1886 | break; |
1887 | case 'S': |
1888 | modifiers |= FORMAT_SESSIONS; |
1889 | break; |
1890 | case 'W': |
1891 | modifiers |= FORMAT_WINDOWS; |
1892 | break; |
1893 | case 'P': |
1894 | modifiers |= FORMAT_PANES; |
1895 | break; |
1896 | } |
1897 | } else if (fm->size == 2) { |
1898 | if (strcmp(fm->modifier, "||" ) == 0 || |
1899 | strcmp(fm->modifier, "&&" ) == 0 || |
1900 | strcmp(fm->modifier, "==" ) == 0 || |
1901 | strcmp(fm->modifier, "!=" ) == 0 || |
1902 | strcmp(fm->modifier, ">=" ) == 0 || |
1903 | strcmp(fm->modifier, "<=" ) == 0) |
1904 | cmp = fm; |
1905 | } |
1906 | } |
1907 | |
1908 | /* Is this a literal string? */ |
1909 | if (modifiers & FORMAT_LITERAL) { |
1910 | value = xstrdup(copy); |
1911 | goto done; |
1912 | } |
1913 | |
1914 | /* Is this a loop, comparison or condition? */ |
1915 | if (modifiers & FORMAT_SESSIONS) { |
1916 | value = format_loop_sessions(ft, copy); |
1917 | if (value == NULL) |
1918 | goto fail; |
1919 | } else if (modifiers & FORMAT_WINDOWS) { |
1920 | value = format_loop_windows(ft, copy); |
1921 | if (value == NULL) |
1922 | goto fail; |
1923 | } else if (modifiers & FORMAT_PANES) { |
1924 | value = format_loop_panes(ft, copy); |
1925 | if (value == NULL) |
1926 | goto fail; |
1927 | } else if (search != NULL) { |
1928 | /* Search in pane. */ |
1929 | new = format_expand(ft, copy); |
1930 | if (wp == NULL) { |
1931 | format_log(ft, "search '%s' but no pane" , new); |
1932 | value = xstrdup("0" ); |
1933 | } else { |
1934 | format_log(ft, "search '%s' pane %%%u" , new, wp->id); |
1935 | value = format_search(fm, wp, new); |
1936 | } |
1937 | free(new); |
1938 | } else if (cmp != NULL) { |
1939 | /* Comparison of left and right. */ |
1940 | if (format_choose(ft, copy, &left, &right, 1) != 0) { |
1941 | format_log(ft, "compare %s syntax error: %s" , |
1942 | cmp->modifier, copy); |
1943 | goto fail; |
1944 | } |
1945 | format_log(ft, "compare %s left is: %s" , cmp->modifier, left); |
1946 | format_log(ft, "compare %s right is: %s" , cmp->modifier, right); |
1947 | |
1948 | if (strcmp(cmp->modifier, "||" ) == 0) { |
1949 | if (format_true(left) || format_true(right)) |
1950 | value = xstrdup("1" ); |
1951 | else |
1952 | value = xstrdup("0" ); |
1953 | } else if (strcmp(cmp->modifier, "&&" ) == 0) { |
1954 | if (format_true(left) && format_true(right)) |
1955 | value = xstrdup("1" ); |
1956 | else |
1957 | value = xstrdup("0" ); |
1958 | } else if (strcmp(cmp->modifier, "==" ) == 0) { |
1959 | if (strcmp(left, right) == 0) |
1960 | value = xstrdup("1" ); |
1961 | else |
1962 | value = xstrdup("0" ); |
1963 | } else if (strcmp(cmp->modifier, "!=" ) == 0) { |
1964 | if (strcmp(left, right) != 0) |
1965 | value = xstrdup("1" ); |
1966 | else |
1967 | value = xstrdup("0" ); |
1968 | } else if (strcmp(cmp->modifier, "<" ) == 0) { |
1969 | if (strcmp(left, right) < 0) |
1970 | value = xstrdup("1" ); |
1971 | else |
1972 | value = xstrdup("0" ); |
1973 | } else if (strcmp(cmp->modifier, ">" ) == 0) { |
1974 | if (strcmp(left, right) > 0) |
1975 | value = xstrdup("1" ); |
1976 | else |
1977 | value = xstrdup("0" ); |
1978 | } else if (strcmp(cmp->modifier, "<=" ) == 0) { |
1979 | if (strcmp(left, right) <= 0) |
1980 | value = xstrdup("1" ); |
1981 | else |
1982 | value = xstrdup("0" ); |
1983 | } else if (strcmp(cmp->modifier, ">=" ) == 0) { |
1984 | if (strcmp(left, right) >= 0) |
1985 | value = xstrdup("1" ); |
1986 | else |
1987 | value = xstrdup("0" ); |
1988 | } else if (strcmp(cmp->modifier, "m" ) == 0) |
1989 | value = format_match(cmp, left, right); |
1990 | |
1991 | free(right); |
1992 | free(left); |
1993 | } else if (*copy == '?') { |
1994 | /* Conditional: check first and choose second or third. */ |
1995 | cp = format_skip(copy + 1, "," ); |
1996 | if (cp == NULL) { |
1997 | format_log(ft, "condition syntax error: %s" , copy + 1); |
1998 | goto fail; |
1999 | } |
2000 | condition = xstrndup(copy + 1, cp - (copy + 1)); |
2001 | format_log(ft, "condition is: %s" , condition); |
2002 | |
2003 | found = format_find(ft, condition, modifiers); |
2004 | if (found == NULL) { |
2005 | /* |
2006 | * If the condition not found, try to expand it. If |
2007 | * the expansion doesn't have any effect, then assume |
2008 | * false. |
2009 | */ |
2010 | found = format_expand(ft, condition); |
2011 | if (strcmp(found, condition) == 0) { |
2012 | free(found); |
2013 | found = xstrdup("" ); |
2014 | format_log(ft, "condition '%s' found: %s" , |
2015 | condition, found); |
2016 | } else { |
2017 | format_log(ft, |
2018 | "condition '%s' not found; assuming false" , |
2019 | condition); |
2020 | } |
2021 | } else |
2022 | format_log(ft, "condition '%s' found" , condition); |
2023 | |
2024 | if (format_choose(ft, cp + 1, &left, &right, 0) != 0) { |
2025 | format_log(ft, "condition '%s' syntax error: %s" , |
2026 | condition, cp + 1); |
2027 | free(found); |
2028 | goto fail; |
2029 | } |
2030 | if (format_true(found)) { |
2031 | format_log(ft, "condition '%s' is true" , condition); |
2032 | value = format_expand(ft, left); |
2033 | } else { |
2034 | format_log(ft, "condition '%s' is false" , condition); |
2035 | value = format_expand(ft, right); |
2036 | } |
2037 | free(right); |
2038 | free(left); |
2039 | |
2040 | free(condition); |
2041 | free(found); |
2042 | } else { |
2043 | /* Neither: look up directly. */ |
2044 | value = format_find(ft, copy, modifiers); |
2045 | if (value == NULL) { |
2046 | format_log(ft, "format '%s' not found" , copy); |
2047 | value = xstrdup("" ); |
2048 | } else |
2049 | format_log(ft, "format '%s' found: %s" , copy, value); |
2050 | } |
2051 | |
2052 | done: |
2053 | /* Expand again if required. */ |
2054 | if (modifiers & FORMAT_EXPAND) { |
2055 | new = format_expand(ft, value); |
2056 | free(value); |
2057 | value = new; |
2058 | } |
2059 | else if (modifiers & FORMAT_EXPANDTIME) { |
2060 | new = format_expand_time(ft, value); |
2061 | free(value); |
2062 | value = new; |
2063 | } |
2064 | |
2065 | /* Perform substitution if any. */ |
2066 | for (i = 0; i < nsub; i++) { |
2067 | left = format_expand(ft, sub[i]->argv[0]); |
2068 | right = format_expand(ft, sub[i]->argv[1]); |
2069 | new = format_sub(sub[i], value, left, right); |
2070 | format_log(ft, "substitute '%s' to '%s': %s" , left, right, new); |
2071 | free(value); |
2072 | value = new; |
2073 | free(right); |
2074 | free(left); |
2075 | } |
2076 | |
2077 | /* Truncate the value if needed. */ |
2078 | if (limit > 0) { |
2079 | new = format_trim_left(value, limit); |
2080 | if (marker != NULL && strcmp(new, value) != 0) { |
2081 | free(value); |
2082 | xasprintf(&value, "%s%s" , new, marker); |
2083 | } else { |
2084 | free(value); |
2085 | value = new; |
2086 | } |
2087 | format_log(ft, "applied length limit %d: %s" , limit, value); |
2088 | } else if (limit < 0) { |
2089 | new = format_trim_right(value, -limit); |
2090 | if (marker != NULL && strcmp(new, value) != 0) { |
2091 | free(value); |
2092 | xasprintf(&value, "%s%s" , marker, new); |
2093 | } else { |
2094 | free(value); |
2095 | value = new; |
2096 | } |
2097 | format_log(ft, "applied length limit %d: %s" , limit, value); |
2098 | } |
2099 | |
2100 | /* Pad the value if needed. */ |
2101 | if (width > 0) { |
2102 | new = utf8_padcstr(value, width); |
2103 | free(value); |
2104 | value = new; |
2105 | format_log(ft, "applied padding width %d: %s" , width, value); |
2106 | } else if (width < 0) { |
2107 | new = utf8_rpadcstr(value, -width); |
2108 | free(value); |
2109 | value = new; |
2110 | format_log(ft, "applied padding width %d: %s" , width, value); |
2111 | } |
2112 | |
2113 | /* Expand the buffer and copy in the value. */ |
2114 | valuelen = strlen(value); |
2115 | while (*len - *off < valuelen + 1) { |
2116 | *buf = xreallocarray(*buf, 2, *len); |
2117 | *len *= 2; |
2118 | } |
2119 | memcpy(*buf + *off, value, valuelen); |
2120 | *off += valuelen; |
2121 | |
2122 | format_log(ft, "replaced '%s' with '%s'" , copy0, value); |
2123 | free(value); |
2124 | |
2125 | free(sub); |
2126 | format_free_modifiers(list, count); |
2127 | free(copy0); |
2128 | return (0); |
2129 | |
2130 | fail: |
2131 | format_log(ft, "failed %s" , copy0); |
2132 | |
2133 | free(sub); |
2134 | format_free_modifiers(list, count); |
2135 | free(copy0); |
2136 | return (-1); |
2137 | } |
2138 | |
2139 | /* Expand keys in a template. */ |
2140 | static char * |
2141 | format_expand1(struct format_tree *ft, const char *fmt, int time) |
2142 | { |
2143 | char *buf, *out, *name; |
2144 | const char *ptr, *s; |
2145 | size_t off, len, n, outlen; |
2146 | int ch, brackets; |
2147 | struct tm *tm; |
2148 | char expanded[8192]; |
2149 | |
2150 | if (fmt == NULL || *fmt == '\0') |
2151 | return (xstrdup("" )); |
2152 | |
2153 | if (ft->loop == FORMAT_LOOP_LIMIT) |
2154 | return (xstrdup("" )); |
2155 | ft->loop++; |
2156 | |
2157 | format_log(ft, "expanding format: %s" , fmt); |
2158 | |
2159 | if (time) { |
2160 | tm = localtime(&ft->time); |
2161 | if (strftime(expanded, sizeof expanded, fmt, tm) == 0) { |
2162 | format_log(ft, "format is too long" ); |
2163 | return (xstrdup("" )); |
2164 | } |
2165 | if (format_logging(ft) && strcmp(expanded, fmt) != 0) |
2166 | format_log(ft, "after time expanded: %s" , expanded); |
2167 | fmt = expanded; |
2168 | } |
2169 | |
2170 | len = 64; |
2171 | buf = xmalloc(len); |
2172 | off = 0; |
2173 | |
2174 | while (*fmt != '\0') { |
2175 | if (*fmt != '#') { |
2176 | while (len - off < 2) { |
2177 | buf = xreallocarray(buf, 2, len); |
2178 | len *= 2; |
2179 | } |
2180 | buf[off++] = *fmt++; |
2181 | continue; |
2182 | } |
2183 | fmt++; |
2184 | |
2185 | ch = (u_char)*fmt++; |
2186 | switch (ch) { |
2187 | case '(': |
2188 | brackets = 1; |
2189 | for (ptr = fmt; *ptr != '\0'; ptr++) { |
2190 | if (*ptr == '(') |
2191 | brackets++; |
2192 | if (*ptr == ')' && --brackets == 0) |
2193 | break; |
2194 | } |
2195 | if (*ptr != ')' || brackets != 0) |
2196 | break; |
2197 | n = ptr - fmt; |
2198 | |
2199 | name = xstrndup(fmt, n); |
2200 | format_log(ft, "found #(): %s" , name); |
2201 | |
2202 | if (ft->flags & FORMAT_NOJOBS) { |
2203 | out = xstrdup("" ); |
2204 | format_log(ft, "#() is disabled" ); |
2205 | } else { |
2206 | out = format_job_get(ft, name); |
2207 | format_log(ft, "#() result: %s" , out); |
2208 | } |
2209 | free(name); |
2210 | |
2211 | outlen = strlen(out); |
2212 | while (len - off < outlen + 1) { |
2213 | buf = xreallocarray(buf, 2, len); |
2214 | len *= 2; |
2215 | } |
2216 | memcpy(buf + off, out, outlen); |
2217 | off += outlen; |
2218 | |
2219 | free(out); |
2220 | |
2221 | fmt += n + 1; |
2222 | continue; |
2223 | case '{': |
2224 | ptr = format_skip((char *)fmt - 2, "}" ); |
2225 | if (ptr == NULL) |
2226 | break; |
2227 | n = ptr - fmt; |
2228 | |
2229 | format_log(ft, "found #{}: %.*s" , (int)n, fmt); |
2230 | if (format_replace(ft, fmt, n, &buf, &len, &off) != 0) |
2231 | break; |
2232 | fmt += n + 1; |
2233 | continue; |
2234 | case '}': |
2235 | case '#': |
2236 | case ',': |
2237 | format_log(ft, "found #%c" , ch); |
2238 | while (len - off < 2) { |
2239 | buf = xreallocarray(buf, 2, len); |
2240 | len *= 2; |
2241 | } |
2242 | buf[off++] = ch; |
2243 | continue; |
2244 | default: |
2245 | s = NULL; |
2246 | if (ch >= 'A' && ch <= 'Z') |
2247 | s = format_upper[ch - 'A']; |
2248 | else if (ch >= 'a' && ch <= 'z') |
2249 | s = format_lower[ch - 'a']; |
2250 | if (s == NULL) { |
2251 | while (len - off < 3) { |
2252 | buf = xreallocarray(buf, 2, len); |
2253 | len *= 2; |
2254 | } |
2255 | buf[off++] = '#'; |
2256 | buf[off++] = ch; |
2257 | continue; |
2258 | } |
2259 | n = strlen(s); |
2260 | format_log(ft, "found #%c: %s" , ch, s); |
2261 | if (format_replace(ft, s, n, &buf, &len, &off) != 0) |
2262 | break; |
2263 | continue; |
2264 | } |
2265 | |
2266 | break; |
2267 | } |
2268 | buf[off] = '\0'; |
2269 | |
2270 | format_log(ft, "result is: %s" , buf); |
2271 | ft->loop--; |
2272 | |
2273 | return (buf); |
2274 | } |
2275 | |
2276 | /* Expand keys in a template, passing through strftime first. */ |
2277 | char * |
2278 | format_expand_time(struct format_tree *ft, const char *fmt) |
2279 | { |
2280 | return (format_expand1(ft, fmt, 1)); |
2281 | } |
2282 | |
2283 | /* Expand keys in a template. */ |
2284 | char * |
2285 | format_expand(struct format_tree *ft, const char *fmt) |
2286 | { |
2287 | return (format_expand1(ft, fmt, 0)); |
2288 | } |
2289 | |
2290 | /* Expand a single string. */ |
2291 | char * |
2292 | format_single(struct cmdq_item *item, const char *fmt, struct client *c, |
2293 | struct session *s, struct winlink *wl, struct window_pane *wp) |
2294 | { |
2295 | struct format_tree *ft; |
2296 | char *expanded; |
2297 | |
2298 | if (item != NULL) |
2299 | ft = format_create(item->client, item, FORMAT_NONE, 0); |
2300 | else |
2301 | ft = format_create(NULL, item, FORMAT_NONE, 0); |
2302 | format_defaults(ft, c, s, wl, wp); |
2303 | |
2304 | expanded = format_expand(ft, fmt); |
2305 | format_free(ft); |
2306 | return (expanded); |
2307 | } |
2308 | |
2309 | /* Set defaults for any of arguments that are not NULL. */ |
2310 | void |
2311 | format_defaults(struct format_tree *ft, struct client *c, struct session *s, |
2312 | struct winlink *wl, struct window_pane *wp) |
2313 | { |
2314 | if (c != NULL && c->name != NULL) |
2315 | log_debug("%s: c=%s" , __func__, c->name); |
2316 | else |
2317 | log_debug("%s: c=none" , __func__); |
2318 | if (s != NULL) |
2319 | log_debug("%s: s=$%u" , __func__, s->id); |
2320 | else |
2321 | log_debug("%s: s=none" , __func__); |
2322 | if (wl != NULL) |
2323 | log_debug("%s: wl=%u w=@%u" , __func__, wl->idx, wl->window->id); |
2324 | else |
2325 | log_debug("%s: wl=none" , __func__); |
2326 | if (wp != NULL) |
2327 | log_debug("%s: wp=%%%u" , __func__, wp->id); |
2328 | else |
2329 | log_debug("%s: wp=none" , __func__); |
2330 | |
2331 | if (c != NULL && s != NULL && c->session != s) |
2332 | log_debug("%s: session does not match" , __func__); |
2333 | |
2334 | format_add(ft, "session_format" , "%d" , s != NULL); |
2335 | format_add(ft, "window_format" , "%d" , wl != NULL); |
2336 | format_add(ft, "pane_format" , "%d" , wp != NULL); |
2337 | |
2338 | if (s == NULL && c != NULL) |
2339 | s = c->session; |
2340 | if (wl == NULL && s != NULL) |
2341 | wl = s->curw; |
2342 | if (wp == NULL && wl != NULL) |
2343 | wp = wl->window->active; |
2344 | |
2345 | if (c != NULL) |
2346 | format_defaults_client(ft, c); |
2347 | if (s != NULL) |
2348 | format_defaults_session(ft, s); |
2349 | if (wl != NULL) |
2350 | format_defaults_winlink(ft, wl); |
2351 | if (wp != NULL) |
2352 | format_defaults_pane(ft, wp); |
2353 | } |
2354 | |
2355 | /* Set default format keys for a session. */ |
2356 | static void |
2357 | format_defaults_session(struct format_tree *ft, struct session *s) |
2358 | { |
2359 | struct session_group *sg; |
2360 | |
2361 | ft->s = s; |
2362 | |
2363 | format_add(ft, "session_name" , "%s" , s->name); |
2364 | format_add(ft, "session_windows" , "%u" , winlink_count(&s->windows)); |
2365 | format_add(ft, "session_id" , "$%u" , s->id); |
2366 | |
2367 | sg = session_group_contains(s); |
2368 | format_add(ft, "session_grouped" , "%d" , sg != NULL); |
2369 | if (sg != NULL) { |
2370 | format_add(ft, "session_group" , "%s" , sg->name); |
2371 | format_add(ft, "session_group_size" , "%u" , |
2372 | session_group_count (sg)); |
2373 | format_add(ft, "session_group_attached" , "%u" , |
2374 | session_group_attached_count (sg)); |
2375 | format_add(ft, "session_group_many_attached" , "%u" , |
2376 | session_group_attached_count (sg) > 1); |
2377 | format_add_cb(ft, "session_group_list" , |
2378 | format_cb_session_group_list); |
2379 | format_add_cb(ft, "session_group_attached_list" , |
2380 | format_cb_session_group_attached_list); |
2381 | } |
2382 | |
2383 | format_add_tv(ft, "session_created" , &s->creation_time); |
2384 | format_add_tv(ft, "session_last_attached" , &s->last_attached_time); |
2385 | format_add_tv(ft, "session_activity" , &s->activity_time); |
2386 | |
2387 | format_add(ft, "session_attached" , "%u" , s->attached); |
2388 | format_add(ft, "session_many_attached" , "%d" , s->attached > 1); |
2389 | format_add_cb(ft, "session_attached_list" , |
2390 | format_cb_session_attached_list); |
2391 | |
2392 | format_add_cb(ft, "session_alerts" , format_cb_session_alerts); |
2393 | format_add_cb(ft, "session_stack" , format_cb_session_stack); |
2394 | } |
2395 | |
2396 | /* Set default format keys for a client. */ |
2397 | static void |
2398 | format_defaults_client(struct format_tree *ft, struct client *c) |
2399 | { |
2400 | struct session *s; |
2401 | const char *name; |
2402 | struct tty *tty = &c->tty; |
2403 | |
2404 | if (ft->s == NULL) |
2405 | ft->s = c->session; |
2406 | ft->c = c; |
2407 | |
2408 | format_add(ft, "client_name" , "%s" , c->name); |
2409 | format_add(ft, "client_pid" , "%ld" , (long) c->pid); |
2410 | format_add(ft, "client_height" , "%u" , tty->sy); |
2411 | format_add(ft, "client_width" , "%u" , tty->sx); |
2412 | format_add(ft, "client_cell_width" , "%u" , tty->xpixel); |
2413 | format_add(ft, "client_cell_height" , "%u" , tty->ypixel); |
2414 | format_add(ft, "client_tty" , "%s" , c->ttyname); |
2415 | format_add(ft, "client_control_mode" , "%d" , |
2416 | !!(c->flags & CLIENT_CONTROL)); |
2417 | |
2418 | if (tty->term_name != NULL) |
2419 | format_add(ft, "client_termname" , "%s" , tty->term_name); |
2420 | |
2421 | format_add_tv(ft, "client_created" , &c->creation_time); |
2422 | format_add_tv(ft, "client_activity" , &c->activity_time); |
2423 | |
2424 | format_add(ft, "client_written" , "%zu" , c->written); |
2425 | format_add(ft, "client_discarded" , "%zu" , c->discarded); |
2426 | |
2427 | name = server_client_get_key_table(c); |
2428 | if (strcmp(c->keytable->name, name) == 0) |
2429 | format_add(ft, "client_prefix" , "%d" , 0); |
2430 | else |
2431 | format_add(ft, "client_prefix" , "%d" , 1); |
2432 | format_add(ft, "client_key_table" , "%s" , c->keytable->name); |
2433 | |
2434 | if (tty->flags & TTY_UTF8) |
2435 | format_add(ft, "client_utf8" , "%d" , 1); |
2436 | else |
2437 | format_add(ft, "client_utf8" , "%d" , 0); |
2438 | |
2439 | if (c->flags & CLIENT_READONLY) |
2440 | format_add(ft, "client_readonly" , "%d" , 1); |
2441 | else |
2442 | format_add(ft, "client_readonly" , "%d" , 0); |
2443 | |
2444 | s = c->session; |
2445 | if (s != NULL) |
2446 | format_add(ft, "client_session" , "%s" , s->name); |
2447 | s = c->last_session; |
2448 | if (s != NULL && session_alive(s)) |
2449 | format_add(ft, "client_last_session" , "%s" , s->name); |
2450 | } |
2451 | |
2452 | /* Set default format keys for a window. */ |
2453 | void |
2454 | format_defaults_window(struct format_tree *ft, struct window *w) |
2455 | { |
2456 | ft->w = w; |
2457 | |
2458 | format_add_tv(ft, "window_activity" , &w->activity_time); |
2459 | format_add(ft, "window_id" , "@%u" , w->id); |
2460 | format_add(ft, "window_name" , "%s" , w->name); |
2461 | format_add(ft, "window_width" , "%u" , w->sx); |
2462 | format_add(ft, "window_height" , "%u" , w->sy); |
2463 | format_add(ft, "window_cell_width" , "%u" , w->xpixel); |
2464 | format_add(ft, "window_cell_height" , "%u" , w->ypixel); |
2465 | format_add_cb(ft, "window_layout" , format_cb_window_layout); |
2466 | format_add_cb(ft, "window_visible_layout" , |
2467 | format_cb_window_visible_layout); |
2468 | format_add(ft, "window_panes" , "%u" , window_count_panes(w)); |
2469 | format_add(ft, "window_zoomed_flag" , "%d" , |
2470 | !!(w->flags & WINDOW_ZOOMED)); |
2471 | } |
2472 | |
2473 | /* Set default format keys for a winlink. */ |
2474 | static void |
2475 | format_defaults_winlink(struct format_tree *ft, struct winlink *wl) |
2476 | { |
2477 | struct client *c = ft->c; |
2478 | struct session *s = wl->session; |
2479 | struct window *w = wl->window; |
2480 | int flag; |
2481 | u_int ox, oy, sx, sy; |
2482 | |
2483 | if (ft->w == NULL) |
2484 | ft->w = wl->window; |
2485 | ft->wl = wl; |
2486 | |
2487 | format_defaults_window(ft, w); |
2488 | |
2489 | if (c != NULL) { |
2490 | flag = tty_window_offset(&c->tty, &ox, &oy, &sx, &sy); |
2491 | format_add(ft, "window_bigger" , "%d" , flag); |
2492 | if (flag) { |
2493 | format_add(ft, "window_offset_x" , "%u" , ox); |
2494 | format_add(ft, "window_offset_y" , "%u" , oy); |
2495 | } |
2496 | } |
2497 | |
2498 | format_add(ft, "window_index" , "%d" , wl->idx); |
2499 | format_add_cb(ft, "window_stack_index" , format_cb_window_stack_index); |
2500 | format_add(ft, "window_flags" , "%s" , window_printable_flags(wl)); |
2501 | format_add(ft, "window_active" , "%d" , wl == s->curw); |
2502 | format_add_cb(ft, "window_active_sessions" , |
2503 | format_cb_window_active_sessions); |
2504 | format_add_cb(ft, "window_active_sessions_list" , |
2505 | format_cb_window_active_sessions_list); |
2506 | format_add_cb(ft, "window_active_clients" , |
2507 | format_cb_window_active_clients); |
2508 | format_add_cb(ft, "window_active_clients_list" , |
2509 | format_cb_window_active_clients_list); |
2510 | |
2511 | format_add(ft, "window_start_flag" , "%d" , |
2512 | !!(wl == RB_MIN(winlinks, &s->windows))); |
2513 | format_add(ft, "window_end_flag" , "%d" , |
2514 | !!(wl == RB_MAX(winlinks, &s->windows))); |
2515 | |
2516 | if (server_check_marked() && marked_pane.wl == wl) |
2517 | format_add(ft, "window_marked_flag" , "1" ); |
2518 | else |
2519 | format_add(ft, "window_marked_flag" , "0" ); |
2520 | |
2521 | format_add(ft, "window_bell_flag" , "%d" , |
2522 | !!(wl->flags & WINLINK_BELL)); |
2523 | format_add(ft, "window_activity_flag" , "%d" , |
2524 | !!(wl->flags & WINLINK_ACTIVITY)); |
2525 | format_add(ft, "window_silence_flag" , "%d" , |
2526 | !!(wl->flags & WINLINK_SILENCE)); |
2527 | format_add(ft, "window_last_flag" , "%d" , |
2528 | !!(wl == TAILQ_FIRST(&s->lastw))); |
2529 | format_add(ft, "window_linked" , "%d" , session_is_linked(s, wl->window)); |
2530 | |
2531 | format_add_cb(ft, "window_linked_sessions_list" , |
2532 | format_cb_window_linked_sessions_list); |
2533 | format_add(ft, "window_linked_sessions" , "%u" , |
2534 | wl->window->references); |
2535 | } |
2536 | |
2537 | /* Set default format keys for a window pane. */ |
2538 | void |
2539 | format_defaults_pane(struct format_tree *ft, struct window_pane *wp) |
2540 | { |
2541 | struct window *w = wp->window; |
2542 | struct grid *gd = wp->base.grid; |
2543 | int status = wp->status; |
2544 | u_int idx; |
2545 | struct window_mode_entry *wme; |
2546 | |
2547 | if (ft->w == NULL) |
2548 | ft->w = w; |
2549 | ft->wp = wp; |
2550 | |
2551 | format_add(ft, "history_size" , "%u" , gd->hsize); |
2552 | format_add(ft, "history_limit" , "%u" , gd->hlimit); |
2553 | format_add_cb(ft, "history_bytes" , format_cb_history_bytes); |
2554 | |
2555 | if (window_pane_index(wp, &idx) != 0) |
2556 | fatalx("index not found" ); |
2557 | format_add(ft, "pane_index" , "%u" , idx); |
2558 | |
2559 | format_add(ft, "pane_width" , "%u" , wp->sx); |
2560 | format_add(ft, "pane_height" , "%u" , wp->sy); |
2561 | format_add(ft, "pane_title" , "%s" , wp->base.title); |
2562 | if (wp->base.path != NULL) |
2563 | format_add(ft, "pane_path" , "%s" , wp->base.path); |
2564 | format_add(ft, "pane_id" , "%%%u" , wp->id); |
2565 | format_add(ft, "pane_active" , "%d" , wp == w->active); |
2566 | format_add(ft, "pane_input_off" , "%d" , !!(wp->flags & PANE_INPUTOFF)); |
2567 | format_add(ft, "pane_pipe" , "%d" , wp->pipe_fd != -1); |
2568 | |
2569 | if ((wp->flags & PANE_STATUSREADY) && WIFEXITED(status)) |
2570 | format_add(ft, "pane_dead_status" , "%d" , WEXITSTATUS(status)); |
2571 | if (~wp->flags & PANE_EMPTY) |
2572 | format_add(ft, "pane_dead" , "%d" , wp->fd == -1); |
2573 | else |
2574 | format_add(ft, "pane_dead" , "0" ); |
2575 | |
2576 | if (server_check_marked() && marked_pane.wp == wp) |
2577 | format_add(ft, "pane_marked" , "1" ); |
2578 | else |
2579 | format_add(ft, "pane_marked" , "0" ); |
2580 | format_add(ft, "pane_marked_set" , "%d" , server_check_marked()); |
2581 | |
2582 | format_add(ft, "pane_left" , "%u" , wp->xoff); |
2583 | format_add(ft, "pane_top" , "%u" , wp->yoff); |
2584 | format_add(ft, "pane_right" , "%u" , wp->xoff + wp->sx - 1); |
2585 | format_add(ft, "pane_bottom" , "%u" , wp->yoff + wp->sy - 1); |
2586 | format_add(ft, "pane_at_left" , "%d" , wp->xoff == 0); |
2587 | format_add_cb(ft, "pane_at_top" , format_cb_pane_at_top); |
2588 | format_add(ft, "pane_at_right" , "%d" , wp->xoff + wp->sx == w->sx); |
2589 | format_add_cb(ft, "pane_at_bottom" , format_cb_pane_at_bottom); |
2590 | |
2591 | wme = TAILQ_FIRST(&wp->modes); |
2592 | if (wme != NULL) { |
2593 | format_add(ft, "pane_mode" , "%s" , wme->mode->name); |
2594 | if (wme->mode->formats != NULL) |
2595 | wme->mode->formats(wme, ft); |
2596 | } |
2597 | format_add_cb(ft, "pane_in_mode" , format_cb_pane_in_mode); |
2598 | |
2599 | format_add(ft, "pane_synchronized" , "%d" , |
2600 | !!options_get_number(w->options, "synchronize-panes" )); |
2601 | if (wp->searchstr != NULL) |
2602 | format_add(ft, "pane_search_string" , "%s" , wp->searchstr); |
2603 | |
2604 | format_add(ft, "pane_tty" , "%s" , wp->tty); |
2605 | format_add(ft, "pane_pid" , "%ld" , (long) wp->pid); |
2606 | format_add_cb(ft, "pane_start_command" , format_cb_start_command); |
2607 | format_add_cb(ft, "pane_current_command" , format_cb_current_command); |
2608 | format_add_cb(ft, "pane_current_path" , format_cb_current_path); |
2609 | |
2610 | format_add(ft, "cursor_x" , "%u" , wp->base.cx); |
2611 | format_add(ft, "cursor_y" , "%u" , wp->base.cy); |
2612 | format_add_cb(ft, "cursor_character" , format_cb_cursor_character); |
2613 | |
2614 | format_add(ft, "scroll_region_upper" , "%u" , wp->base.rupper); |
2615 | format_add(ft, "scroll_region_lower" , "%u" , wp->base.rlower); |
2616 | |
2617 | format_add(ft, "alternate_on" , "%d" , wp->saved_grid ? 1 : 0); |
2618 | format_add(ft, "alternate_saved_x" , "%u" , wp->saved_cx); |
2619 | format_add(ft, "alternate_saved_y" , "%u" , wp->saved_cy); |
2620 | |
2621 | format_add(ft, "cursor_flag" , "%d" , |
2622 | !!(wp->base.mode & MODE_CURSOR)); |
2623 | format_add(ft, "insert_flag" , "%d" , |
2624 | !!(wp->base.mode & MODE_INSERT)); |
2625 | format_add(ft, "keypad_cursor_flag" , "%d" , |
2626 | !!(wp->base.mode & MODE_KCURSOR)); |
2627 | format_add(ft, "keypad_flag" , "%d" , |
2628 | !!(wp->base.mode & MODE_KKEYPAD)); |
2629 | format_add(ft, "wrap_flag" , "%d" , |
2630 | !!(wp->base.mode & MODE_WRAP)); |
2631 | format_add(ft, "origin_flag" , "%d" , |
2632 | !!(wp->base.mode & MODE_ORIGIN)); |
2633 | |
2634 | format_add(ft, "mouse_any_flag" , "%d" , |
2635 | !!(wp->base.mode & ALL_MOUSE_MODES)); |
2636 | format_add(ft, "mouse_standard_flag" , "%d" , |
2637 | !!(wp->base.mode & MODE_MOUSE_STANDARD)); |
2638 | format_add(ft, "mouse_button_flag" , "%d" , |
2639 | !!(wp->base.mode & MODE_MOUSE_BUTTON)); |
2640 | format_add(ft, "mouse_all_flag" , "%d" , |
2641 | !!(wp->base.mode & MODE_MOUSE_ALL)); |
2642 | format_add(ft, "mouse_utf8_flag" , "%d" , |
2643 | !!(wp->base.mode & MODE_MOUSE_UTF8)); |
2644 | format_add(ft, "mouse_sgr_flag" , "%d" , |
2645 | !!(wp->base.mode & MODE_MOUSE_SGR)); |
2646 | |
2647 | format_add_cb(ft, "pane_tabs" , format_cb_pane_tabs); |
2648 | } |
2649 | |
2650 | /* Set default format keys for paste buffer. */ |
2651 | void |
2652 | format_defaults_paste_buffer(struct format_tree *ft, struct paste_buffer *pb) |
2653 | { |
2654 | struct timeval tv; |
2655 | size_t size; |
2656 | char *s; |
2657 | |
2658 | timerclear(&tv); |
2659 | tv.tv_sec = paste_buffer_created(pb); |
2660 | paste_buffer_data(pb, &size); |
2661 | |
2662 | format_add(ft, "buffer_size" , "%zu" , size); |
2663 | format_add(ft, "buffer_name" , "%s" , paste_buffer_name(pb)); |
2664 | format_add_tv(ft, "buffer_created" , &tv); |
2665 | |
2666 | s = paste_make_sample(pb); |
2667 | format_add(ft, "buffer_sample" , "%s" , s); |
2668 | free(s); |
2669 | } |
2670 | |