Annotation of src/usr.bin/tmux/format.c, Revision 1.25
1.25 ! nicm 1: /* $OpenBSD: format.c,v 1.24 2013/04/17 14:52:31 deraadt 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.3 nicm 349: format_add(ft, "client_tty", "%s", c->tty.path);
350: format_add(ft, "client_termname", "%s", c->tty.termname);
351:
352: t = c->creation_time.tv_sec;
1.24 deraadt 353: format_add(ft, "client_created", "%lld", (long long) t);
1.3 nicm 354: tim = ctime(&t);
355: *strchr(tim, '\n') = '\0';
356: format_add(ft, "client_created_string", "%s", tim);
357:
358: t = c->activity_time.tv_sec;
1.24 deraadt 359: format_add(ft, "client_activity", "%lld", (long long) t);
1.3 nicm 360: tim = ctime(&t);
361: *strchr(tim, '\n') = '\0';
362: format_add(ft, "client_activity_string", "%s", tim);
1.14 nicm 363:
364: format_add(ft, "client_prefix", "%d", !!(c->flags & CLIENT_PREFIX));
1.3 nicm 365:
366: if (c->tty.flags & TTY_UTF8)
367: format_add(ft, "client_utf8", "%d", 1);
368: else
369: format_add(ft, "client_utf8", "%d", 0);
370:
371: if (c->flags & CLIENT_READONLY)
372: format_add(ft, "client_readonly", "%d", 1);
373: else
374: format_add(ft, "client_readonly", "%d", 0);
1.15 nicm 375:
376: s = c->session;
377: if (s != NULL)
378: format_add(ft, "client_session", "%s", s->name);
379: s = c->last_session;
380: if (s != NULL && session_alive(s))
381: format_add(ft, "client_last_session", "%s", s->name);
1.1 nicm 382: }
383:
384: /* Set default format keys for a winlink. */
385: void
386: format_winlink(struct format_tree *ft, struct session *s, struct winlink *wl)
387: {
388: struct window *w = wl->window;
389: char *layout, *flags;
390:
391: layout = layout_dump(w);
392: flags = window_printable_flags(s, wl);
393:
1.5 nicm 394: format_add(ft, "window_id", "@%u", w->id);
1.1 nicm 395: format_add(ft, "window_index", "%d", wl->idx);
396: format_add(ft, "window_name", "%s", w->name);
397: format_add(ft, "window_width", "%u", w->sx);
398: format_add(ft, "window_height", "%u", w->sy);
399: format_add(ft, "window_flags", "%s", flags);
400: format_add(ft, "window_layout", "%s", layout);
401: format_add(ft, "window_active", "%d", wl == s->curw);
1.8 nicm 402: format_add(ft, "window_panes", "%u", window_count_panes(w));
1.1 nicm 403:
1.9 nicm 404: free(flags);
405: free(layout);
1.1 nicm 406: }
407:
1.19 nicm 408: /* Add window pane tabs. */
409: void
410: format_window_pane_tabs(struct format_tree *ft, struct window_pane *wp)
411: {
412: struct evbuffer *buffer;
413: u_int i;
414:
415: buffer = evbuffer_new();
416: for (i = 0; i < wp->base.grid->sx; i++) {
417: if (!bit_test(wp->base.tabs, i))
418: continue;
419:
420: if (EVBUFFER_LENGTH(buffer) > 0)
421: evbuffer_add(buffer, ",", 1);
422: evbuffer_add_printf(buffer, "%d", i);
423: }
424:
425: format_add(ft, "pane_tabs", "%.*s", (int) EVBUFFER_LENGTH(buffer),
426: EVBUFFER_DATA(buffer));
427: evbuffer_free(buffer);
428: }
429:
1.1 nicm 430: /* Set default format keys for a window pane. */
431: void
432: format_window_pane(struct format_tree *ft, struct window_pane *wp)
433: {
434: struct grid *gd = wp->base.grid;
435: struct grid_line *gl;
436: unsigned long long size;
1.20 nicm 437: u_int i, idx;
1.21 nicm 438: const char *cwd;
439: char *cmd;
1.1 nicm 440:
441: size = 0;
442: for (i = 0; i < gd->hsize; i++) {
443: gl = &gd->linedata[i];
444: size += gl->cellsize * sizeof *gl->celldata;
445: }
446: size += gd->hsize * sizeof *gd->linedata;
1.16 nicm 447: format_add(ft, "history_size", "%u", gd->hsize);
448: format_add(ft, "history_limit", "%u", gd->hlimit);
449: format_add(ft, "history_bytes", "%llu", size);
1.1 nicm 450:
1.4 nicm 451: if (window_pane_index(wp, &idx) != 0)
452: fatalx("index not found");
1.16 nicm 453: format_add(ft, "pane_index", "%u", idx);
1.4 nicm 454:
1.1 nicm 455: format_add(ft, "pane_width", "%u", wp->sx);
456: format_add(ft, "pane_height", "%u", wp->sy);
457: format_add(ft, "pane_title", "%s", wp->base.title);
458: format_add(ft, "pane_id", "%%%u", wp->id);
459: format_add(ft, "pane_active", "%d", wp == wp->window->active);
460: format_add(ft, "pane_dead", "%d", wp->fd == -1);
1.16 nicm 461:
462: format_add(ft, "pane_in_mode", "%d", wp->screen != &wp->base);
463:
464: if (wp->tty != NULL)
465: format_add(ft, "pane_tty", "%s", wp->tty);
466: format_add(ft, "pane_pid", "%ld", (long) wp->pid);
1.2 nicm 467: if (wp->cmd != NULL)
468: format_add(ft, "pane_start_command", "%s", wp->cmd);
469: if (wp->cwd != NULL)
470: format_add(ft, "pane_start_path", "%s", wp->cwd);
1.12 nicm 471: if ((cwd = get_proc_cwd(wp->fd)) != NULL)
472: format_add(ft, "pane_current_path", "%s", cwd);
1.21 nicm 473: if ((cmd = get_proc_name(wp->fd, wp->tty)) != NULL) {
1.17 nicm 474: format_add(ft, "pane_current_command", "%s", cmd);
1.21 nicm 475: free(cmd);
476: }
1.16 nicm 477:
478: format_add(ft, "cursor_x", "%d", wp->base.cx);
479: format_add(ft, "cursor_y", "%d", wp->base.cy);
480: format_add(ft, "scroll_region_upper", "%d", wp->base.rupper);
481: format_add(ft, "scroll_region_lower", "%d", wp->base.rlower);
482: format_add(ft, "saved_cursor_x", "%d", wp->ictx.old_cx);
483: format_add(ft, "saved_cursor_y", "%d", wp->ictx.old_cy);
484:
485: format_add(ft, "alternate_on", "%d", wp->saved_grid ? 1 : 0);
486: format_add(ft, "alternate_saved_x", "%d", wp->saved_cx);
487: format_add(ft, "alternate_saved_y", "%d", wp->saved_cy);
488:
489: format_add(ft, "cursor_flag", "%d",
490: !!(wp->base.mode & MODE_CURSOR));
491: format_add(ft, "insert_flag", "%d",
492: !!(wp->base.mode & MODE_INSERT));
493: format_add(ft, "keypad_cursor_flag", "%d",
494: !!(wp->base.mode & MODE_KCURSOR));
495: format_add(ft, "keypad_flag", "%d",
496: !!(wp->base.mode & MODE_KKEYPAD));
497: format_add(ft, "wrap_flag", "%d",
498: !!(wp->base.mode & MODE_WRAP));
499:
500: format_add(ft, "mouse_standard_flag", "%d",
501: !!(wp->base.mode & MODE_MOUSE_STANDARD));
502: format_add(ft, "mouse_button_flag", "%d",
503: !!(wp->base.mode & MODE_MOUSE_BUTTON));
504: format_add(ft, "mouse_any_flag", "%d",
505: !!(wp->base.mode & MODE_MOUSE_ANY));
506: format_add(ft, "mouse_utf8_flag", "%d",
507: !!(wp->base.mode & MODE_MOUSE_UTF8));
1.19 nicm 508:
509: format_window_pane_tabs(ft, wp);
1.8 nicm 510: }
511:
1.19 nicm 512: /* Set default format keys for paste buffer. */
1.8 nicm 513: void
514: format_paste_buffer(struct format_tree *ft, struct paste_buffer *pb)
515: {
516: char *pb_print = paste_print(pb, 50);
517:
518: format_add(ft, "buffer_size", "%zu", pb->size);
519: format_add(ft, "buffer_sample", "%s", pb_print);
520:
1.9 nicm 521: free(pb_print);
1.1 nicm 522: }