Annotation of src/usr.bin/tmux/format.c, Revision 1.27
1.27 ! nicm 1: /* $OpenBSD: format.c,v 1.26 2013/07/05 15:27:14 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;
154: va_list ap;
155:
156: fe = xmalloc(sizeof *fe);
157: fe->key = xstrdup(key);
158:
159: va_start(ap, fmt);
160: xvasprintf(&fe->value, fmt, ap);
161: va_end(ap);
162:
163: RB_INSERT(format_tree, ft, fe);
164: }
165:
166: /* Find a format entry. */
167: const char *
168: format_find(struct format_tree *ft, const char *key)
169: {
170: struct format_entry *fe, fe_find;
171:
172: fe_find.key = (char *) key;
173: fe = RB_FIND(format_tree, ft, &fe_find);
174: if (fe == NULL)
175: return (NULL);
176: return (fe->value);
177: }
178:
179: /*
180: * Replace a key/value pair in buffer. #{blah} is expanded directly,
181: * #{?blah,a,b} is replace with a if blah exists and is nonzero else b.
182: */
183: int
184: format_replace(struct format_tree *ft,
185: const char *key, size_t keylen, char **buf, size_t *len, size_t *off)
186: {
187: char *copy, *ptr;
188: const char *value;
189: size_t valuelen;
190:
191: /* Make a copy of the key. */
192: copy = xmalloc(keylen + 1);
193: memcpy(copy, key, keylen);
194: copy[keylen] = '\0';
195:
196: /*
197: * Is this a conditional? If so, check it exists and extract either the
198: * first or second element. If not, look up the key directly.
199: */
200: if (*copy == '?') {
201: ptr = strchr(copy, ',');
202: if (ptr == NULL)
203: goto fail;
204: *ptr = '\0';
205:
206: value = format_find(ft, copy + 1);
207: if (value != NULL && (value[0] != '0' || value[1] != '\0')) {
208: value = ptr + 1;
209: ptr = strchr(value, ',');
210: if (ptr == NULL)
211: goto fail;
212: *ptr = '\0';
213: } else {
214: ptr = strchr(ptr + 1, ',');
215: if (ptr == NULL)
216: goto fail;
217: value = ptr + 1;
218: }
219: } else {
220: value = format_find(ft, copy);
221: if (value == NULL)
222: value = "";
223: }
224: valuelen = strlen(value);
225:
226: /* Expand the buffer and copy in the value. */
227: while (*len - *off < valuelen + 1) {
228: *buf = xrealloc(*buf, 2, *len);
229: *len *= 2;
230: }
231: memcpy(*buf + *off, value, valuelen);
232: *off += valuelen;
233:
1.9 nicm 234: free(copy);
1.1 nicm 235: return (0);
236:
237: fail:
1.9 nicm 238: free(copy);
1.1 nicm 239: return (-1);
240: }
241:
242: /* Expand keys in a template. */
243: char *
244: format_expand(struct format_tree *ft, const char *fmt)
245: {
246: char *buf, *ptr;
247: const char *s;
248: size_t off, len, n;
249: int ch;
250:
251: len = 64;
252: buf = xmalloc(len);
253: off = 0;
254:
255: while (*fmt != '\0') {
256: if (*fmt != '#') {
257: while (len - off < 2) {
258: buf = xrealloc(buf, 2, len);
259: len *= 2;
260: }
261: buf[off++] = *fmt++;
262: continue;
263: }
264: fmt++;
265:
266: ch = (u_char) *fmt++;
1.25 nicm 267:
1.1 nicm 268: switch (ch) {
269: case '{':
270: ptr = strchr(fmt, '}');
271: if (ptr == NULL)
272: break;
273: n = ptr - fmt;
274:
275: if (format_replace(ft, fmt, n, &buf, &len, &off) != 0)
276: break;
277: fmt += n + 1;
278: continue;
279: default:
1.25 nicm 280: s = NULL;
281: if (ch >= 'A' && ch <= 'Z')
282: s = format_upper[ch - 'A'];
283: else if (ch >= 'a' && ch <= 'z')
284: s = format_lower[ch - 'a'];
285: if (s == NULL) {
286: while (len - off < 3) {
287: buf = xrealloc(buf, 2, len);
288: len *= 2;
1.1 nicm 289: }
1.25 nicm 290: buf[off++] = '#';
291: buf[off++] = ch;
292: continue;
1.1 nicm 293: }
1.25 nicm 294: n = strlen(s);
295: if (format_replace(ft, s, n, &buf, &len, &off) != 0)
296: break;
1.1 nicm 297: continue;
298: }
299:
300: break;
301: }
302: buf[off] = '\0';
303:
304: return (buf);
305: }
306:
307: /* Set default format keys for a session. */
308: void
309: format_session(struct format_tree *ft, struct session *s)
310: {
311: struct session_group *sg;
312: char *tim;
313: time_t t;
314:
315: format_add(ft, "session_name", "%s", s->name);
316: format_add(ft, "session_windows", "%u", winlink_count(&s->windows));
317: format_add(ft, "session_width", "%u", s->sx);
318: format_add(ft, "session_height", "%u", s->sy);
1.23 nicm 319: format_add(ft, "session_id", "$%u", s->id);
1.1 nicm 320:
321: sg = session_group_find(s);
322: format_add(ft, "session_grouped", "%d", sg != NULL);
323: if (sg != NULL)
324: format_add(ft, "session_group", "%u", session_group_index(sg));
325:
326: t = s->creation_time.tv_sec;
1.24 deraadt 327: format_add(ft, "session_created", "%lld", (long long) t);
1.1 nicm 328: tim = ctime(&t);
329: *strchr(tim, '\n') = '\0';
330: format_add(ft, "session_created_string", "%s", tim);
331:
332: if (s->flags & SESSION_UNATTACHED)
333: format_add(ft, "session_attached", "%d", 0);
334: else
335: format_add(ft, "session_attached", "%d", 1);
1.3 nicm 336: }
337:
338: /* Set default format keys for a client. */
339: void
340: format_client(struct format_tree *ft, struct client *c)
341: {
1.15 nicm 342: char *tim;
343: time_t t;
344: struct session *s;
1.3 nicm 345:
346: format_add(ft, "client_cwd", "%s", c->cwd);
1.6 nicm 347: format_add(ft, "client_height", "%u", c->tty.sy);
348: format_add(ft, "client_width", "%u", c->tty.sx);
1.27 ! nicm 349: if (c->tty.path != NULL)
! 350: format_add(ft, "client_tty", "%s", c->tty.path);
! 351: if (c->tty.termname != NULL)
! 352: format_add(ft, "client_termname", "%s", c->tty.termname);
1.3 nicm 353:
354: t = c->creation_time.tv_sec;
1.24 deraadt 355: format_add(ft, "client_created", "%lld", (long long) t);
1.3 nicm 356: tim = ctime(&t);
357: *strchr(tim, '\n') = '\0';
358: format_add(ft, "client_created_string", "%s", tim);
359:
360: t = c->activity_time.tv_sec;
1.24 deraadt 361: format_add(ft, "client_activity", "%lld", (long long) t);
1.3 nicm 362: tim = ctime(&t);
363: *strchr(tim, '\n') = '\0';
364: format_add(ft, "client_activity_string", "%s", tim);
1.14 nicm 365:
366: format_add(ft, "client_prefix", "%d", !!(c->flags & CLIENT_PREFIX));
1.3 nicm 367:
368: if (c->tty.flags & TTY_UTF8)
369: format_add(ft, "client_utf8", "%d", 1);
370: else
371: format_add(ft, "client_utf8", "%d", 0);
372:
373: if (c->flags & CLIENT_READONLY)
374: format_add(ft, "client_readonly", "%d", 1);
375: else
376: format_add(ft, "client_readonly", "%d", 0);
1.15 nicm 377:
378: s = c->session;
379: if (s != NULL)
380: format_add(ft, "client_session", "%s", s->name);
381: s = c->last_session;
382: if (s != NULL && session_alive(s))
383: format_add(ft, "client_last_session", "%s", s->name);
1.1 nicm 384: }
385:
386: /* Set default format keys for a winlink. */
387: void
388: format_winlink(struct format_tree *ft, struct session *s, struct winlink *wl)
389: {
390: struct window *w = wl->window;
391: char *layout, *flags;
392:
393: layout = layout_dump(w);
394: flags = window_printable_flags(s, wl);
395:
1.5 nicm 396: format_add(ft, "window_id", "@%u", w->id);
1.1 nicm 397: format_add(ft, "window_index", "%d", wl->idx);
398: format_add(ft, "window_name", "%s", w->name);
399: format_add(ft, "window_width", "%u", w->sx);
400: format_add(ft, "window_height", "%u", w->sy);
401: format_add(ft, "window_flags", "%s", flags);
402: format_add(ft, "window_layout", "%s", layout);
403: format_add(ft, "window_active", "%d", wl == s->curw);
1.8 nicm 404: format_add(ft, "window_panes", "%u", window_count_panes(w));
1.1 nicm 405:
1.9 nicm 406: free(flags);
407: free(layout);
1.1 nicm 408: }
409:
1.19 nicm 410: /* Add window pane tabs. */
411: void
412: format_window_pane_tabs(struct format_tree *ft, struct window_pane *wp)
413: {
414: struct evbuffer *buffer;
415: u_int i;
416:
417: buffer = evbuffer_new();
418: for (i = 0; i < wp->base.grid->sx; i++) {
419: if (!bit_test(wp->base.tabs, i))
420: continue;
421:
422: if (EVBUFFER_LENGTH(buffer) > 0)
423: evbuffer_add(buffer, ",", 1);
424: evbuffer_add_printf(buffer, "%d", i);
425: }
426:
427: format_add(ft, "pane_tabs", "%.*s", (int) EVBUFFER_LENGTH(buffer),
428: EVBUFFER_DATA(buffer));
429: evbuffer_free(buffer);
430: }
431:
1.1 nicm 432: /* Set default format keys for a window pane. */
433: void
434: format_window_pane(struct format_tree *ft, struct window_pane *wp)
435: {
436: struct grid *gd = wp->base.grid;
437: struct grid_line *gl;
438: unsigned long long size;
1.20 nicm 439: u_int i, idx;
1.21 nicm 440: const char *cwd;
441: char *cmd;
1.1 nicm 442:
443: size = 0;
444: for (i = 0; i < gd->hsize; i++) {
445: gl = &gd->linedata[i];
446: size += gl->cellsize * sizeof *gl->celldata;
447: }
448: size += gd->hsize * sizeof *gd->linedata;
1.16 nicm 449: format_add(ft, "history_size", "%u", gd->hsize);
450: format_add(ft, "history_limit", "%u", gd->hlimit);
451: format_add(ft, "history_bytes", "%llu", size);
1.1 nicm 452:
1.4 nicm 453: if (window_pane_index(wp, &idx) != 0)
454: fatalx("index not found");
1.16 nicm 455: format_add(ft, "pane_index", "%u", idx);
1.4 nicm 456:
1.1 nicm 457: format_add(ft, "pane_width", "%u", wp->sx);
458: format_add(ft, "pane_height", "%u", wp->sy);
459: format_add(ft, "pane_title", "%s", wp->base.title);
460: format_add(ft, "pane_id", "%%%u", wp->id);
461: format_add(ft, "pane_active", "%d", wp == wp->window->active);
462: format_add(ft, "pane_dead", "%d", wp->fd == -1);
1.16 nicm 463:
464: format_add(ft, "pane_in_mode", "%d", wp->screen != &wp->base);
1.26 nicm 465: format_add(ft, "pane_synchronized", "%d",
466: !!options_get_number(&wp->window->options, "synchronize-panes"));
1.16 nicm 467:
468: if (wp->tty != NULL)
469: format_add(ft, "pane_tty", "%s", wp->tty);
470: format_add(ft, "pane_pid", "%ld", (long) wp->pid);
1.2 nicm 471: if (wp->cmd != NULL)
472: format_add(ft, "pane_start_command", "%s", wp->cmd);
473: if (wp->cwd != NULL)
474: format_add(ft, "pane_start_path", "%s", wp->cwd);
1.12 nicm 475: if ((cwd = get_proc_cwd(wp->fd)) != NULL)
476: format_add(ft, "pane_current_path", "%s", cwd);
1.21 nicm 477: if ((cmd = get_proc_name(wp->fd, wp->tty)) != NULL) {
1.17 nicm 478: format_add(ft, "pane_current_command", "%s", cmd);
1.21 nicm 479: free(cmd);
480: }
1.16 nicm 481:
482: format_add(ft, "cursor_x", "%d", wp->base.cx);
483: format_add(ft, "cursor_y", "%d", wp->base.cy);
484: format_add(ft, "scroll_region_upper", "%d", wp->base.rupper);
485: format_add(ft, "scroll_region_lower", "%d", wp->base.rlower);
486: format_add(ft, "saved_cursor_x", "%d", wp->ictx.old_cx);
487: format_add(ft, "saved_cursor_y", "%d", wp->ictx.old_cy);
488:
489: format_add(ft, "alternate_on", "%d", wp->saved_grid ? 1 : 0);
490: format_add(ft, "alternate_saved_x", "%d", wp->saved_cx);
491: format_add(ft, "alternate_saved_y", "%d", wp->saved_cy);
492:
493: format_add(ft, "cursor_flag", "%d",
494: !!(wp->base.mode & MODE_CURSOR));
495: format_add(ft, "insert_flag", "%d",
496: !!(wp->base.mode & MODE_INSERT));
497: format_add(ft, "keypad_cursor_flag", "%d",
498: !!(wp->base.mode & MODE_KCURSOR));
499: format_add(ft, "keypad_flag", "%d",
500: !!(wp->base.mode & MODE_KKEYPAD));
501: format_add(ft, "wrap_flag", "%d",
502: !!(wp->base.mode & MODE_WRAP));
503:
504: format_add(ft, "mouse_standard_flag", "%d",
505: !!(wp->base.mode & MODE_MOUSE_STANDARD));
506: format_add(ft, "mouse_button_flag", "%d",
507: !!(wp->base.mode & MODE_MOUSE_BUTTON));
508: format_add(ft, "mouse_any_flag", "%d",
509: !!(wp->base.mode & MODE_MOUSE_ANY));
510: format_add(ft, "mouse_utf8_flag", "%d",
511: !!(wp->base.mode & MODE_MOUSE_UTF8));
1.19 nicm 512:
513: format_window_pane_tabs(ft, wp);
1.8 nicm 514: }
515:
1.19 nicm 516: /* Set default format keys for paste buffer. */
1.8 nicm 517: void
518: format_paste_buffer(struct format_tree *ft, struct paste_buffer *pb)
519: {
520: char *pb_print = paste_print(pb, 50);
521:
522: format_add(ft, "buffer_size", "%zu", pb->size);
523: format_add(ft, "buffer_sample", "%s", pb_print);
524:
1.9 nicm 525: free(pb_print);
1.1 nicm 526: }