Annotation of src/usr.bin/tmux/format.c, Revision 1.29
1.29 ! nicm 1: /* $OpenBSD: format.c,v 1.28 2013/10/10 11:47:28 nicm Exp $ */
1.1 nicm 2:
3: /*
4: * Copyright (c) 2011 Nicholas Marriott <nicm@users.sourceforge.net>
5: *
6: * Permission to use, copy, modify, and distribute this software for any
7: * purpose with or without fee is hereby granted, provided that the above
8: * copyright notice and this permission notice appear in all copies.
9: *
10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14: * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15: * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16: * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17: */
18:
19: #include <sys/types.h>
20:
21: #include <netdb.h>
22: #include <stdarg.h>
1.9 nicm 23: #include <stdlib.h>
1.1 nicm 24: #include <string.h>
25: #include <time.h>
26: #include <unistd.h>
27:
28: #include "tmux.h"
29:
30: /*
31: * Build a list of key-value pairs and use them to expand #{key} entries in a
32: * string.
33: */
34:
1.19 nicm 35: int format_replace(struct format_tree *, const char *, size_t, char **,
36: size_t *, size_t *);
37: void format_window_pane_tabs(struct format_tree *, struct window_pane *);
1.1 nicm 38:
39: /* Format key-value replacement entry. */
40: RB_GENERATE(format_tree, format_entry, entry, format_cmp);
41:
42: /* Format tree comparison function. */
43: int
44: format_cmp(struct format_entry *fe1, struct format_entry *fe2)
45: {
46: return (strcmp(fe1->key, fe2->key));
47: }
48:
1.25 nicm 49: /* Single-character uppercase aliases. */
50: const char *format_upper[] = {
1.1 nicm 51: NULL, /* A */
52: NULL, /* B */
53: NULL, /* C */
54: "pane_id", /* D */
55: NULL, /* E */
56: "window_flags", /* F */
57: NULL, /* G */
58: "host", /* H */
59: "window_index", /* I */
60: NULL, /* J */
61: NULL, /* K */
62: NULL, /* L */
63: NULL, /* M */
64: NULL, /* N */
65: NULL, /* O */
66: "pane_index", /* P */
67: NULL, /* Q */
68: NULL, /* R */
69: "session_name", /* S */
70: "pane_title", /* T */
71: NULL, /* U */
72: NULL, /* V */
73: "window_name", /* W */
74: NULL, /* X */
75: NULL, /* Y */
76: NULL /* Z */
77: };
78:
1.25 nicm 79: /* Single-character lowercase aliases. */
80: const char *format_lower[] = {
81: NULL, /* a */
82: NULL, /* b */
83: NULL, /* c */
84: NULL, /* d */
85: NULL, /* e */
86: NULL, /* f */
87: NULL, /* g */
88: "host_short", /* h */
89: NULL, /* i */
90: NULL, /* j */
91: NULL, /* k */
92: NULL, /* l */
93: NULL, /* m */
94: NULL, /* n */
95: NULL, /* o */
96: NULL, /* p */
97: NULL, /* q */
98: NULL, /* r */
99: NULL, /* s */
100: NULL, /* t */
101: NULL, /* u */
102: NULL, /* v */
103: NULL, /* w */
104: NULL, /* x */
105: NULL, /* y */
106: NULL /* z */
107: };
108:
1.1 nicm 109: /* Create a new tree. */
110: struct format_tree *
111: format_create(void)
112: {
113: struct format_tree *ft;
1.25 nicm 114: char host[MAXHOSTNAMELEN], *ptr;
1.1 nicm 115:
116: ft = xmalloc(sizeof *ft);
117: RB_INIT(ft);
118:
1.25 nicm 119: if (gethostname(host, sizeof host) == 0) {
1.1 nicm 120: format_add(ft, "host", "%s", host);
1.25 nicm 121: if ((ptr = strrchr(host, '.')) != NULL)
122: *ptr = '\0';
123: format_add(ft, "host_short", "%s", host);
124: }
1.1 nicm 125:
126: return (ft);
127: }
128:
129: /* Free a tree. */
130: void
131: format_free(struct format_tree *ft)
132: {
133: struct format_entry *fe, *fe_next;
134:
135: fe_next = RB_MIN(format_tree, ft);
136: while (fe_next != NULL) {
137: fe = fe_next;
138: fe_next = RB_NEXT(format_tree, ft, fe);
139:
140: RB_REMOVE(format_tree, ft, fe);
1.9 nicm 141: free(fe->value);
142: free(fe->key);
143: free(fe);
1.1 nicm 144: }
145:
1.25 nicm 146: free(ft);
1.1 nicm 147: }
148:
149: /* Add a key-value pair. */
150: void
151: format_add(struct format_tree *ft, const char *key, const char *fmt, ...)
152: {
153: struct format_entry *fe;
1.28 nicm 154: struct format_entry *fe_now;
1.1 nicm 155: va_list ap;
156:
157: fe = xmalloc(sizeof *fe);
158: fe->key = xstrdup(key);
159:
160: va_start(ap, fmt);
161: xvasprintf(&fe->value, fmt, ap);
162: va_end(ap);
163:
1.28 nicm 164: fe_now = RB_INSERT(format_tree, ft, fe);
165: if (fe_now != NULL) {
166: free(fe_now->value);
167: fe_now->value = fe->value;
168: free(fe->key);
169: free(fe);
170: }
1.1 nicm 171: }
172:
173: /* Find a format entry. */
174: const char *
175: format_find(struct format_tree *ft, const char *key)
176: {
177: struct format_entry *fe, fe_find;
178:
179: fe_find.key = (char *) key;
180: fe = RB_FIND(format_tree, ft, &fe_find);
181: if (fe == NULL)
182: return (NULL);
183: return (fe->value);
184: }
185:
186: /*
187: * Replace a key/value pair in buffer. #{blah} is expanded directly,
188: * #{?blah,a,b} is replace with a if blah exists and is nonzero else b.
189: */
190: int
191: format_replace(struct format_tree *ft,
192: const char *key, size_t keylen, char **buf, size_t *len, size_t *off)
193: {
194: char *copy, *ptr;
195: const char *value;
196: size_t valuelen;
197:
198: /* Make a copy of the key. */
199: copy = xmalloc(keylen + 1);
200: memcpy(copy, key, keylen);
201: copy[keylen] = '\0';
202:
203: /*
204: * Is this a conditional? If so, check it exists and extract either the
205: * first or second element. If not, look up the key directly.
206: */
207: if (*copy == '?') {
208: ptr = strchr(copy, ',');
209: if (ptr == NULL)
210: goto fail;
211: *ptr = '\0';
212:
213: value = format_find(ft, copy + 1);
214: if (value != NULL && (value[0] != '0' || value[1] != '\0')) {
215: value = ptr + 1;
216: ptr = strchr(value, ',');
217: if (ptr == NULL)
218: goto fail;
219: *ptr = '\0';
220: } else {
221: ptr = strchr(ptr + 1, ',');
222: if (ptr == NULL)
223: goto fail;
224: value = ptr + 1;
225: }
226: } else {
227: value = format_find(ft, copy);
228: if (value == NULL)
229: value = "";
230: }
231: valuelen = strlen(value);
232:
233: /* Expand the buffer and copy in the value. */
234: while (*len - *off < valuelen + 1) {
235: *buf = xrealloc(*buf, 2, *len);
236: *len *= 2;
237: }
238: memcpy(*buf + *off, value, valuelen);
239: *off += valuelen;
240:
1.9 nicm 241: free(copy);
1.1 nicm 242: return (0);
243:
244: fail:
1.9 nicm 245: free(copy);
1.1 nicm 246: return (-1);
247: }
248:
249: /* Expand keys in a template. */
250: char *
251: format_expand(struct format_tree *ft, const char *fmt)
252: {
253: char *buf, *ptr;
254: const char *s;
255: size_t off, len, n;
256: int ch;
257:
258: len = 64;
259: buf = xmalloc(len);
260: off = 0;
261:
262: while (*fmt != '\0') {
263: if (*fmt != '#') {
264: while (len - off < 2) {
265: buf = xrealloc(buf, 2, len);
266: len *= 2;
267: }
268: buf[off++] = *fmt++;
269: continue;
270: }
271: fmt++;
272:
273: ch = (u_char) *fmt++;
1.25 nicm 274:
1.1 nicm 275: switch (ch) {
276: case '{':
277: ptr = strchr(fmt, '}');
278: if (ptr == NULL)
279: break;
280: n = ptr - fmt;
281:
282: if (format_replace(ft, fmt, n, &buf, &len, &off) != 0)
283: break;
284: fmt += n + 1;
285: continue;
286: default:
1.25 nicm 287: s = NULL;
288: if (ch >= 'A' && ch <= 'Z')
289: s = format_upper[ch - 'A'];
290: else if (ch >= 'a' && ch <= 'z')
291: s = format_lower[ch - 'a'];
292: if (s == NULL) {
293: while (len - off < 3) {
294: buf = xrealloc(buf, 2, len);
295: len *= 2;
1.1 nicm 296: }
1.25 nicm 297: buf[off++] = '#';
298: buf[off++] = ch;
299: continue;
1.1 nicm 300: }
1.25 nicm 301: n = strlen(s);
302: if (format_replace(ft, s, n, &buf, &len, &off) != 0)
303: break;
1.1 nicm 304: continue;
305: }
306:
307: break;
308: }
309: buf[off] = '\0';
310:
311: return (buf);
312: }
313:
314: /* Set default format keys for a session. */
315: void
316: format_session(struct format_tree *ft, struct session *s)
317: {
318: struct session_group *sg;
319: char *tim;
320: time_t t;
321:
322: format_add(ft, "session_name", "%s", s->name);
323: format_add(ft, "session_windows", "%u", winlink_count(&s->windows));
324: format_add(ft, "session_width", "%u", s->sx);
325: format_add(ft, "session_height", "%u", s->sy);
1.23 nicm 326: format_add(ft, "session_id", "$%u", s->id);
1.1 nicm 327:
328: sg = session_group_find(s);
329: format_add(ft, "session_grouped", "%d", sg != NULL);
330: if (sg != NULL)
331: format_add(ft, "session_group", "%u", session_group_index(sg));
332:
333: t = s->creation_time.tv_sec;
1.24 deraadt 334: format_add(ft, "session_created", "%lld", (long long) t);
1.1 nicm 335: tim = ctime(&t);
336: *strchr(tim, '\n') = '\0';
337: format_add(ft, "session_created_string", "%s", tim);
338:
339: if (s->flags & SESSION_UNATTACHED)
340: format_add(ft, "session_attached", "%d", 0);
341: else
342: format_add(ft, "session_attached", "%d", 1);
1.3 nicm 343: }
344:
345: /* Set default format keys for a client. */
346: void
347: format_client(struct format_tree *ft, struct client *c)
348: {
1.15 nicm 349: char *tim;
350: time_t t;
351: struct session *s;
1.3 nicm 352:
353: format_add(ft, "client_cwd", "%s", c->cwd);
1.6 nicm 354: format_add(ft, "client_height", "%u", c->tty.sy);
355: format_add(ft, "client_width", "%u", c->tty.sx);
1.27 nicm 356: if (c->tty.path != NULL)
357: format_add(ft, "client_tty", "%s", c->tty.path);
358: if (c->tty.termname != NULL)
359: format_add(ft, "client_termname", "%s", c->tty.termname);
1.3 nicm 360:
361: t = c->creation_time.tv_sec;
1.24 deraadt 362: format_add(ft, "client_created", "%lld", (long long) t);
1.3 nicm 363: tim = ctime(&t);
364: *strchr(tim, '\n') = '\0';
365: format_add(ft, "client_created_string", "%s", tim);
366:
367: t = c->activity_time.tv_sec;
1.24 deraadt 368: format_add(ft, "client_activity", "%lld", (long long) t);
1.3 nicm 369: tim = ctime(&t);
370: *strchr(tim, '\n') = '\0';
371: format_add(ft, "client_activity_string", "%s", tim);
1.14 nicm 372:
373: format_add(ft, "client_prefix", "%d", !!(c->flags & CLIENT_PREFIX));
1.3 nicm 374:
375: if (c->tty.flags & TTY_UTF8)
376: format_add(ft, "client_utf8", "%d", 1);
377: else
378: format_add(ft, "client_utf8", "%d", 0);
379:
380: if (c->flags & CLIENT_READONLY)
381: format_add(ft, "client_readonly", "%d", 1);
382: else
383: format_add(ft, "client_readonly", "%d", 0);
1.15 nicm 384:
385: s = c->session;
386: if (s != NULL)
387: format_add(ft, "client_session", "%s", s->name);
388: s = c->last_session;
389: if (s != NULL && session_alive(s))
390: format_add(ft, "client_last_session", "%s", s->name);
1.1 nicm 391: }
392:
393: /* Set default format keys for a winlink. */
394: void
395: format_winlink(struct format_tree *ft, struct session *s, struct winlink *wl)
396: {
397: struct window *w = wl->window;
398: char *layout, *flags;
399:
400: layout = layout_dump(w);
401: flags = window_printable_flags(s, wl);
402:
1.5 nicm 403: format_add(ft, "window_id", "@%u", w->id);
1.1 nicm 404: format_add(ft, "window_index", "%d", wl->idx);
405: format_add(ft, "window_name", "%s", w->name);
406: format_add(ft, "window_width", "%u", w->sx);
407: format_add(ft, "window_height", "%u", w->sy);
408: format_add(ft, "window_flags", "%s", flags);
409: format_add(ft, "window_layout", "%s", layout);
410: format_add(ft, "window_active", "%d", wl == s->curw);
1.8 nicm 411: format_add(ft, "window_panes", "%u", window_count_panes(w));
1.29 ! nicm 412:
! 413: format_add(ft, "window_bell_flag", "%u",
! 414: !!(wl->flags & WINLINK_BELL));
! 415: format_add(ft, "window_content_flag", "%u",
! 416: !!(wl->flags & WINLINK_CONTENT));
! 417: format_add(ft, "window_activity_flag", "%u",
! 418: !!(wl->flags & WINLINK_ACTIVITY));
! 419: format_add(ft, "window_silence_flag", "%u",
! 420: !!(wl->flags & WINLINK_SILENCE));
1.1 nicm 421:
1.9 nicm 422: free(flags);
423: free(layout);
1.1 nicm 424: }
425:
1.19 nicm 426: /* Add window pane tabs. */
427: void
428: format_window_pane_tabs(struct format_tree *ft, struct window_pane *wp)
429: {
430: struct evbuffer *buffer;
431: u_int i;
432:
433: buffer = evbuffer_new();
434: for (i = 0; i < wp->base.grid->sx; i++) {
435: if (!bit_test(wp->base.tabs, i))
436: continue;
437:
438: if (EVBUFFER_LENGTH(buffer) > 0)
439: evbuffer_add(buffer, ",", 1);
440: evbuffer_add_printf(buffer, "%d", i);
441: }
442:
443: format_add(ft, "pane_tabs", "%.*s", (int) EVBUFFER_LENGTH(buffer),
444: EVBUFFER_DATA(buffer));
445: evbuffer_free(buffer);
446: }
447:
1.1 nicm 448: /* Set default format keys for a window pane. */
449: void
450: format_window_pane(struct format_tree *ft, struct window_pane *wp)
451: {
452: struct grid *gd = wp->base.grid;
453: struct grid_line *gl;
454: unsigned long long size;
1.20 nicm 455: u_int i, idx;
1.21 nicm 456: const char *cwd;
457: char *cmd;
1.1 nicm 458:
459: size = 0;
460: for (i = 0; i < gd->hsize; i++) {
461: gl = &gd->linedata[i];
462: size += gl->cellsize * sizeof *gl->celldata;
463: }
464: size += gd->hsize * sizeof *gd->linedata;
1.16 nicm 465: format_add(ft, "history_size", "%u", gd->hsize);
466: format_add(ft, "history_limit", "%u", gd->hlimit);
467: format_add(ft, "history_bytes", "%llu", size);
1.1 nicm 468:
1.4 nicm 469: if (window_pane_index(wp, &idx) != 0)
470: fatalx("index not found");
1.16 nicm 471: format_add(ft, "pane_index", "%u", idx);
1.4 nicm 472:
1.1 nicm 473: format_add(ft, "pane_width", "%u", wp->sx);
474: format_add(ft, "pane_height", "%u", wp->sy);
475: format_add(ft, "pane_title", "%s", wp->base.title);
476: format_add(ft, "pane_id", "%%%u", wp->id);
477: format_add(ft, "pane_active", "%d", wp == wp->window->active);
478: format_add(ft, "pane_dead", "%d", wp->fd == -1);
1.16 nicm 479:
480: format_add(ft, "pane_in_mode", "%d", wp->screen != &wp->base);
1.26 nicm 481: format_add(ft, "pane_synchronized", "%d",
482: !!options_get_number(&wp->window->options, "synchronize-panes"));
1.16 nicm 483:
484: if (wp->tty != NULL)
485: format_add(ft, "pane_tty", "%s", wp->tty);
486: format_add(ft, "pane_pid", "%ld", (long) wp->pid);
1.2 nicm 487: if (wp->cmd != NULL)
488: format_add(ft, "pane_start_command", "%s", wp->cmd);
489: if (wp->cwd != NULL)
490: format_add(ft, "pane_start_path", "%s", wp->cwd);
1.12 nicm 491: if ((cwd = get_proc_cwd(wp->fd)) != NULL)
492: format_add(ft, "pane_current_path", "%s", cwd);
1.21 nicm 493: if ((cmd = get_proc_name(wp->fd, wp->tty)) != NULL) {
1.17 nicm 494: format_add(ft, "pane_current_command", "%s", cmd);
1.21 nicm 495: free(cmd);
496: }
1.16 nicm 497:
498: format_add(ft, "cursor_x", "%d", wp->base.cx);
499: format_add(ft, "cursor_y", "%d", wp->base.cy);
500: format_add(ft, "scroll_region_upper", "%d", wp->base.rupper);
501: format_add(ft, "scroll_region_lower", "%d", wp->base.rlower);
502: format_add(ft, "saved_cursor_x", "%d", wp->ictx.old_cx);
503: format_add(ft, "saved_cursor_y", "%d", wp->ictx.old_cy);
504:
505: format_add(ft, "alternate_on", "%d", wp->saved_grid ? 1 : 0);
506: format_add(ft, "alternate_saved_x", "%d", wp->saved_cx);
507: format_add(ft, "alternate_saved_y", "%d", wp->saved_cy);
508:
509: format_add(ft, "cursor_flag", "%d",
510: !!(wp->base.mode & MODE_CURSOR));
511: format_add(ft, "insert_flag", "%d",
512: !!(wp->base.mode & MODE_INSERT));
513: format_add(ft, "keypad_cursor_flag", "%d",
514: !!(wp->base.mode & MODE_KCURSOR));
515: format_add(ft, "keypad_flag", "%d",
516: !!(wp->base.mode & MODE_KKEYPAD));
517: format_add(ft, "wrap_flag", "%d",
518: !!(wp->base.mode & MODE_WRAP));
519:
520: format_add(ft, "mouse_standard_flag", "%d",
521: !!(wp->base.mode & MODE_MOUSE_STANDARD));
522: format_add(ft, "mouse_button_flag", "%d",
523: !!(wp->base.mode & MODE_MOUSE_BUTTON));
524: format_add(ft, "mouse_any_flag", "%d",
525: !!(wp->base.mode & MODE_MOUSE_ANY));
526: format_add(ft, "mouse_utf8_flag", "%d",
527: !!(wp->base.mode & MODE_MOUSE_UTF8));
1.19 nicm 528:
529: format_window_pane_tabs(ft, wp);
1.8 nicm 530: }
531:
1.19 nicm 532: /* Set default format keys for paste buffer. */
1.8 nicm 533: void
534: format_paste_buffer(struct format_tree *ft, struct paste_buffer *pb)
535: {
536: char *pb_print = paste_print(pb, 50);
537:
538: format_add(ft, "buffer_size", "%zu", pb->size);
539: format_add(ft, "buffer_sample", "%s", pb_print);
540:
1.9 nicm 541: free(pb_print);
1.1 nicm 542: }