4 * Copyright (c) 2011 Nicholas Marriott <nicm@users.sourceforge.net>
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.
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.
19 #include <sys/types.h>
33 * Build a list of key-value pairs and use them to expand #{key} entries in a
37 int format_replace(struct format_tree
*, const char *, size_t, char **,
39 char *format_get_command(struct window_pane
*);
40 void format_window_pane_tabs(struct format_tree
*, struct window_pane
*);
42 /* Format key-value replacement entry. */
43 RB_GENERATE(format_tree
, format_entry
, entry
, format_cmp
);
45 /* Format tree comparison function. */
47 format_cmp(struct format_entry
*fe1
, struct format_entry
*fe2
)
49 return (strcmp(fe1
->key
, fe2
->key
));
52 /* Single-character uppercase aliases. */
53 const char *format_upper
[] = {
59 "window_flags", /* F */
62 "window_index", /* I */
72 "session_name", /* S */
76 "window_name", /* W */
82 /* Single-character lowercase aliases. */
83 const char *format_lower
[] = {
112 /* Create a new tree. */
116 struct format_tree
*ft
;
117 char host
[MAXHOSTNAMELEN
], *ptr
;
119 ft
= xmalloc(sizeof *ft
);
122 if (gethostname(host
, sizeof host
) == 0) {
123 format_add(ft
, "host", "%s", host
);
124 if ((ptr
= strchr(host
, '.')) != NULL
)
126 format_add(ft
, "host_short", "%s", host
);
134 format_free(struct format_tree
*ft
)
136 struct format_entry
*fe
, *fe_next
;
138 fe_next
= RB_MIN(format_tree
, ft
);
139 while (fe_next
!= NULL
) {
141 fe_next
= RB_NEXT(format_tree
, ft
, fe
);
143 RB_REMOVE(format_tree
, ft
, fe
);
152 /* Add a key-value pair. */
154 format_add(struct format_tree
*ft
, const char *key
, const char *fmt
, ...)
156 struct format_entry
*fe
;
157 struct format_entry
*fe_now
;
160 fe
= xmalloc(sizeof *fe
);
161 fe
->key
= xstrdup(key
);
164 xvasprintf(&fe
->value
, fmt
, ap
);
167 fe_now
= RB_INSERT(format_tree
, ft
, fe
);
168 if (fe_now
!= NULL
) {
170 fe_now
->value
= fe
->value
;
176 /* Find a format entry. */
178 format_find(struct format_tree
*ft
, const char *key
)
180 struct format_entry
*fe
, fe_find
;
182 fe_find
.key
= __UNCONST(key
);
183 fe
= RB_FIND(format_tree
, ft
, &fe_find
);
190 * Replace a key/value pair in buffer. #{blah} is expanded directly,
191 * #{?blah,a,b} is replace with a if blah exists and is nonzero else b.
194 format_replace(struct format_tree
*ft
, const char *key
, size_t keylen
,
195 char **buf
, size_t *len
, size_t *off
)
197 char *copy
, *copy0
, *endptr
, *ptr
, *saved
;
200 u_long limit
= ULONG_MAX
;
202 /* Make a copy of the key. */
203 copy0
= copy
= xmalloc(keylen
+ 1);
204 memcpy(copy
, key
, keylen
);
207 /* Is there a length limit or whatnot? */
208 if (!islower((u_char
) *copy
) && *copy
!= '?') {
209 while (*copy
!= ':' && *copy
!= '\0') {
213 limit
= strtoul(copy
+ 1, &endptr
, 10);
214 if (errno
== ERANGE
&& limit
== ULONG_MAX
)
229 * Is this a conditional? If so, check it exists and extract either the
230 * first or second element. If not, look up the key directly.
233 ptr
= strchr(copy
, ',');
238 value
= format_find(ft
, copy
+ 1);
239 if (value
!= NULL
&& (value
[0] != '0' || value
[1] != '\0')) {
241 ptr
= strchr(value
, ',');
246 ptr
= strchr(ptr
+ 1, ',');
251 saved
= format_expand(ft
, value
);
254 value
= format_find(ft
, copy
);
259 valuelen
= strlen(value
);
261 /* Truncate the value if needed. */
262 if (valuelen
> limit
)
265 /* Expand the buffer and copy in the value. */
266 while (*len
- *off
< valuelen
+ 1) {
267 *buf
= xrealloc(*buf
, 2, *len
);
270 memcpy(*buf
+ *off
, value
, valuelen
);
282 /* Expand keys in a template. */
284 format_expand(struct format_tree
*ft
, const char *fmt
)
295 while (*fmt
!= '\0') {
297 while (len
- off
< 2) {
298 buf
= xrealloc(buf
, 2, len
);
306 ch
= (u_char
) *fmt
++;
310 for (ptr
= fmt
; *ptr
!= '\0'; ptr
++) {
313 if (*ptr
== '}' && --brackets
== 0)
316 if (*ptr
!= '}' || brackets
!= 0)
320 if (format_replace(ft
, fmt
, n
, &buf
, &len
, &off
) != 0)
325 while (len
- off
< 2) {
326 buf
= xrealloc(buf
, 2, len
);
333 if (ch
>= 'A' && ch
<= 'Z')
334 s
= format_upper
[ch
- 'A'];
335 else if (ch
>= 'a' && ch
<= 'z')
336 s
= format_lower
[ch
- 'a'];
338 while (len
- off
< 3) {
339 buf
= xrealloc(buf
, 2, len
);
347 if (format_replace(ft
, s
, n
, &buf
, &len
, &off
) != 0)
359 /* Get command name for format. */
361 format_get_command(struct window_pane
*wp
)
365 cmd
= osdep_get_name(wp
->fd
, wp
->tty
);
366 if (cmd
== NULL
|| *cmd
== '\0') {
368 cmd
= xstrdup(wp
->cmd
);
369 if (cmd
== NULL
|| *cmd
== '\0') {
371 cmd
= xstrdup(wp
->shell
);
374 out
= parse_window_name(cmd
);
379 /* Set default format keys for a session. */
381 format_session(struct format_tree
*ft
, struct session
*s
)
383 struct session_group
*sg
;
387 format_add(ft
, "session_name", "%s", s
->name
);
388 format_add(ft
, "session_windows", "%u", winlink_count(&s
->windows
));
389 format_add(ft
, "session_width", "%u", s
->sx
);
390 format_add(ft
, "session_height", "%u", s
->sy
);
391 format_add(ft
, "session_id", "$%u", s
->id
);
393 sg
= session_group_find(s
);
394 format_add(ft
, "session_grouped", "%d", sg
!= NULL
);
396 format_add(ft
, "session_group", "%u", session_group_index(sg
));
398 t
= s
->creation_time
.tv_sec
;
399 format_add(ft
, "session_created", "%lld", (long long) t
);
401 *strchr(tim
, '\n') = '\0';
402 format_add(ft
, "session_created_string", "%s", tim
);
404 if (s
->flags
& SESSION_UNATTACHED
)
405 format_add(ft
, "session_attached", "%d", 0);
407 format_add(ft
, "session_attached", "%d", 1);
410 /* Set default format keys for a client. */
412 format_client(struct format_tree
*ft
, struct client
*c
)
418 format_add(ft
, "client_height", "%u", c
->tty
.sy
);
419 format_add(ft
, "client_width", "%u", c
->tty
.sx
);
420 if (c
->tty
.path
!= NULL
)
421 format_add(ft
, "client_tty", "%s", c
->tty
.path
);
422 if (c
->tty
.termname
!= NULL
)
423 format_add(ft
, "client_termname", "%s", c
->tty
.termname
);
425 t
= c
->creation_time
.tv_sec
;
426 format_add(ft
, "client_created", "%lld", (long long) t
);
428 *strchr(tim
, '\n') = '\0';
429 format_add(ft
, "client_created_string", "%s", tim
);
431 t
= c
->activity_time
.tv_sec
;
432 format_add(ft
, "client_activity", "%lld", (long long) t
);
434 *strchr(tim
, '\n') = '\0';
435 format_add(ft
, "client_activity_string", "%s", tim
);
437 format_add(ft
, "client_prefix", "%d", !!(c
->flags
& CLIENT_PREFIX
));
439 if (c
->tty
.flags
& TTY_UTF8
)
440 format_add(ft
, "client_utf8", "%d", 1);
442 format_add(ft
, "client_utf8", "%d", 0);
444 if (c
->flags
& CLIENT_READONLY
)
445 format_add(ft
, "client_readonly", "%d", 1);
447 format_add(ft
, "client_readonly", "%d", 0);
451 format_add(ft
, "client_session", "%s", s
->name
);
453 if (s
!= NULL
&& session_alive(s
))
454 format_add(ft
, "client_last_session", "%s", s
->name
);
457 /* Set default format keys for a window. */
459 format_window(struct format_tree
*ft
, struct window
*w
)
463 layout
= layout_dump(w
);
465 format_add(ft
, "window_id", "@%u", w
->id
);
466 format_add(ft
, "window_name", "%s", w
->name
);
467 format_add(ft
, "window_width", "%u", w
->sx
);
468 format_add(ft
, "window_height", "%u", w
->sy
);
469 format_add(ft
, "window_layout", "%s", layout
);
470 format_add(ft
, "window_panes", "%u", window_count_panes(w
));
475 /* Set default format keys for a winlink. */
477 format_winlink(struct format_tree
*ft
, struct session
*s
, struct winlink
*wl
)
479 struct window
*w
= wl
->window
;
482 flags
= window_printable_flags(s
, wl
);
484 format_window(ft
, w
);
486 format_add(ft
, "window_index", "%d", wl
->idx
);
487 format_add(ft
, "window_flags", "%s", flags
);
488 format_add(ft
, "window_active", "%d", wl
== s
->curw
);
490 format_add(ft
, "window_bell_flag", "%u",
491 !!(wl
->flags
& WINLINK_BELL
));
492 format_add(ft
, "window_content_flag", "%u",
493 !!(wl
->flags
& WINLINK_CONTENT
));
494 format_add(ft
, "window_activity_flag", "%u",
495 !!(wl
->flags
& WINLINK_ACTIVITY
));
496 format_add(ft
, "window_silence_flag", "%u",
497 !!(wl
->flags
& WINLINK_SILENCE
));
503 /* Add window pane tabs. */
505 format_window_pane_tabs(struct format_tree
*ft
, struct window_pane
*wp
)
507 struct evbuffer
*buffer
;
510 buffer
= evbuffer_new();
511 for (i
= 0; i
< wp
->base
.grid
->sx
; i
++) {
512 if (!bit_test(wp
->base
.tabs
, i
))
515 if (EVBUFFER_LENGTH(buffer
) > 0)
516 evbuffer_add(buffer
, ",", 1);
517 evbuffer_add_printf(buffer
, "%d", i
);
520 format_add(ft
, "pane_tabs", "%.*s", (int) EVBUFFER_LENGTH(buffer
),
521 EVBUFFER_DATA(buffer
));
522 evbuffer_free(buffer
);
525 /* Set default format keys for a window pane. */
527 format_window_pane(struct format_tree
*ft
, struct window_pane
*wp
)
529 struct grid
*gd
= wp
->base
.grid
;
530 struct grid_line
*gl
;
531 unsigned long long size
;
536 for (i
= 0; i
< gd
->hsize
; i
++) {
537 gl
= &gd
->linedata
[i
];
538 size
+= gl
->cellsize
* sizeof *gl
->celldata
;
540 size
+= gd
->hsize
* sizeof *gd
->linedata
;
541 format_add(ft
, "history_size", "%u", gd
->hsize
);
542 format_add(ft
, "history_limit", "%u", gd
->hlimit
);
543 format_add(ft
, "history_bytes", "%llu", size
);
545 if (window_pane_index(wp
, &idx
) != 0)
546 fatalx("index not found");
547 format_add(ft
, "pane_index", "%u", idx
);
549 format_add(ft
, "pane_width", "%u", wp
->sx
);
550 format_add(ft
, "pane_height", "%u", wp
->sy
);
551 format_add(ft
, "pane_title", "%s", wp
->base
.title
);
552 format_add(ft
, "pane_id", "%%%u", wp
->id
);
553 format_add(ft
, "pane_active", "%d", wp
== wp
->window
->active
);
554 format_add(ft
, "pane_dead", "%d", wp
->fd
== -1);
556 format_add(ft
, "pane_in_mode", "%d", wp
->screen
!= &wp
->base
);
557 format_add(ft
, "pane_synchronized", "%d",
558 !!options_get_number(&wp
->window
->options
, "synchronize-panes"));
560 format_add(ft
, "pane_tty", "%s", wp
->tty
);
561 format_add(ft
, "pane_pid", "%ld", (long) wp
->pid
);
563 format_add(ft
, "pane_start_command", "%s", wp
->cmd
);
564 if ((cwd
= osdep_get_cwd(wp
->fd
)) != NULL
)
565 format_add(ft
, "pane_current_path", "%s", cwd
);
566 if ((cmd
= format_get_command(wp
)) != NULL
) {
567 format_add(ft
, "pane_current_command", "%s", cmd
);
571 format_add(ft
, "cursor_x", "%d", wp
->base
.cx
);
572 format_add(ft
, "cursor_y", "%d", wp
->base
.cy
);
573 format_add(ft
, "scroll_region_upper", "%d", wp
->base
.rupper
);
574 format_add(ft
, "scroll_region_lower", "%d", wp
->base
.rlower
);
575 format_add(ft
, "saved_cursor_x", "%d", wp
->ictx
.old_cx
);
576 format_add(ft
, "saved_cursor_y", "%d", wp
->ictx
.old_cy
);
578 format_add(ft
, "alternate_on", "%d", wp
->saved_grid
? 1 : 0);
579 format_add(ft
, "alternate_saved_x", "%d", wp
->saved_cx
);
580 format_add(ft
, "alternate_saved_y", "%d", wp
->saved_cy
);
582 format_add(ft
, "cursor_flag", "%d",
583 !!(wp
->base
.mode
& MODE_CURSOR
));
584 format_add(ft
, "insert_flag", "%d",
585 !!(wp
->base
.mode
& MODE_INSERT
));
586 format_add(ft
, "keypad_cursor_flag", "%d",
587 !!(wp
->base
.mode
& MODE_KCURSOR
));
588 format_add(ft
, "keypad_flag", "%d",
589 !!(wp
->base
.mode
& MODE_KKEYPAD
));
590 format_add(ft
, "wrap_flag", "%d",
591 !!(wp
->base
.mode
& MODE_WRAP
));
593 format_add(ft
, "mouse_standard_flag", "%d",
594 !!(wp
->base
.mode
& MODE_MOUSE_STANDARD
));
595 format_add(ft
, "mouse_button_flag", "%d",
596 !!(wp
->base
.mode
& MODE_MOUSE_BUTTON
));
597 format_add(ft
, "mouse_any_flag", "%d",
598 !!(wp
->base
.mode
& MODE_MOUSE_ANY
));
599 format_add(ft
, "mouse_utf8_flag", "%d",
600 !!(wp
->base
.mode
& MODE_MOUSE_UTF8
));
602 format_window_pane_tabs(ft
, wp
);
605 /* Set default format keys for paste buffer. */
607 format_paste_buffer(struct format_tree
*ft
, struct paste_buffer
*pb
)
609 char *pb_print
= paste_print(pb
, 50);
611 format_add(ft
, "buffer_size", "%zu", pb
->size
);
612 format_add(ft
, "buffer_sample", "%s", pb_print
);