Annotation of src/usr.bin/tmux/format.c, Revision 1.1
1.1 ! nicm 1: /* $OpenBSD$ */
! 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>
! 23: #include <string.h>
! 24: #include <time.h>
! 25: #include <unistd.h>
! 26:
! 27: #include "tmux.h"
! 28:
! 29: /*
! 30: * Build a list of key-value pairs and use them to expand #{key} entries in a
! 31: * string.
! 32: */
! 33:
! 34: int format_replace(struct format_tree *,
! 35: const char *, size_t, char **, size_t *, size_t *);
! 36:
! 37: /* Format key-value replacement entry. */
! 38: RB_GENERATE(format_tree, format_entry, entry, format_cmp);
! 39:
! 40: /* Format tree comparison function. */
! 41: int
! 42: format_cmp(struct format_entry *fe1, struct format_entry *fe2)
! 43: {
! 44: return (strcmp(fe1->key, fe2->key));
! 45: }
! 46:
! 47: /* Single-character aliases. */
! 48: const char *format_aliases[26] = {
! 49: NULL, /* A */
! 50: NULL, /* B */
! 51: NULL, /* C */
! 52: "pane_id", /* D */
! 53: NULL, /* E */
! 54: "window_flags", /* F */
! 55: NULL, /* G */
! 56: "host", /* H */
! 57: "window_index", /* I */
! 58: NULL, /* J */
! 59: NULL, /* K */
! 60: NULL, /* L */
! 61: NULL, /* M */
! 62: NULL, /* N */
! 63: NULL, /* O */
! 64: "pane_index", /* P */
! 65: NULL, /* Q */
! 66: NULL, /* R */
! 67: "session_name", /* S */
! 68: "pane_title", /* T */
! 69: NULL, /* U */
! 70: NULL, /* V */
! 71: "window_name", /* W */
! 72: NULL, /* X */
! 73: NULL, /* Y */
! 74: NULL /* Z */
! 75: };
! 76:
! 77: /* Create a new tree. */
! 78: struct format_tree *
! 79: format_create(void)
! 80: {
! 81: struct format_tree *ft;
! 82: char host[MAXHOSTNAMELEN];
! 83:
! 84: ft = xmalloc(sizeof *ft);
! 85: RB_INIT(ft);
! 86:
! 87: if (gethostname(host, sizeof host) == 0)
! 88: format_add(ft, "host", "%s", host);
! 89:
! 90: return (ft);
! 91: }
! 92:
! 93: /* Free a tree. */
! 94: void
! 95: format_free(struct format_tree *ft)
! 96: {
! 97: struct format_entry *fe, *fe_next;
! 98:
! 99: fe_next = RB_MIN(format_tree, ft);
! 100: while (fe_next != NULL) {
! 101: fe = fe_next;
! 102: fe_next = RB_NEXT(format_tree, ft, fe);
! 103:
! 104: RB_REMOVE(format_tree, ft, fe);
! 105: xfree(fe->value);
! 106: xfree(fe->key);
! 107: xfree(fe);
! 108: }
! 109:
! 110: xfree (ft);
! 111: }
! 112:
! 113: /* Add a key-value pair. */
! 114: void
! 115: format_add(struct format_tree *ft, const char *key, const char *fmt, ...)
! 116: {
! 117: struct format_entry *fe;
! 118: va_list ap;
! 119:
! 120: fe = xmalloc(sizeof *fe);
! 121: fe->key = xstrdup(key);
! 122:
! 123: va_start(ap, fmt);
! 124: xvasprintf(&fe->value, fmt, ap);
! 125: va_end(ap);
! 126:
! 127: RB_INSERT(format_tree, ft, fe);
! 128: }
! 129:
! 130: /* Find a format entry. */
! 131: const char *
! 132: format_find(struct format_tree *ft, const char *key)
! 133: {
! 134: struct format_entry *fe, fe_find;
! 135:
! 136: fe_find.key = (char *) key;
! 137: fe = RB_FIND(format_tree, ft, &fe_find);
! 138: if (fe == NULL)
! 139: return (NULL);
! 140: return (fe->value);
! 141: }
! 142:
! 143: /*
! 144: * Replace a key/value pair in buffer. #{blah} is expanded directly,
! 145: * #{?blah,a,b} is replace with a if blah exists and is nonzero else b.
! 146: */
! 147: int
! 148: format_replace(struct format_tree *ft,
! 149: const char *key, size_t keylen, char **buf, size_t *len, size_t *off)
! 150: {
! 151: char *copy, *ptr;
! 152: const char *value;
! 153: size_t valuelen;
! 154:
! 155: /* Make a copy of the key. */
! 156: copy = xmalloc(keylen + 1);
! 157: memcpy(copy, key, keylen);
! 158: copy[keylen] = '\0';
! 159:
! 160: /*
! 161: * Is this a conditional? If so, check it exists and extract either the
! 162: * first or second element. If not, look up the key directly.
! 163: */
! 164: if (*copy == '?') {
! 165: ptr = strchr(copy, ',');
! 166: if (ptr == NULL)
! 167: goto fail;
! 168: *ptr = '\0';
! 169:
! 170: value = format_find(ft, copy + 1);
! 171: if (value != NULL && (value[0] != '0' || value[1] != '\0')) {
! 172: value = ptr + 1;
! 173: ptr = strchr(value, ',');
! 174: if (ptr == NULL)
! 175: goto fail;
! 176: *ptr = '\0';
! 177: } else {
! 178: ptr = strchr(ptr + 1, ',');
! 179: if (ptr == NULL)
! 180: goto fail;
! 181: value = ptr + 1;
! 182: }
! 183: } else {
! 184: value = format_find(ft, copy);
! 185: if (value == NULL)
! 186: value = "";
! 187: }
! 188: valuelen = strlen(value);
! 189:
! 190: /* Expand the buffer and copy in the value. */
! 191: while (*len - *off < valuelen + 1) {
! 192: *buf = xrealloc(*buf, 2, *len);
! 193: *len *= 2;
! 194: }
! 195: memcpy(*buf + *off, value, valuelen);
! 196: *off += valuelen;
! 197:
! 198: xfree(copy);
! 199: return (0);
! 200:
! 201: fail:
! 202: xfree(copy);
! 203: return (-1);
! 204: }
! 205:
! 206: /* Expand keys in a template. */
! 207: char *
! 208: format_expand(struct format_tree *ft, const char *fmt)
! 209: {
! 210: char *buf, *ptr;
! 211: const char *s;
! 212: size_t off, len, n;
! 213: int ch;
! 214:
! 215: len = 64;
! 216: buf = xmalloc(len);
! 217: off = 0;
! 218:
! 219: while (*fmt != '\0') {
! 220: if (*fmt != '#') {
! 221: while (len - off < 2) {
! 222: buf = xrealloc(buf, 2, len);
! 223: len *= 2;
! 224: }
! 225: buf[off++] = *fmt++;
! 226: continue;
! 227: }
! 228: fmt++;
! 229:
! 230: ch = (u_char) *fmt++;
! 231: switch (ch) {
! 232: case '{':
! 233: ptr = strchr(fmt, '}');
! 234: if (ptr == NULL)
! 235: break;
! 236: n = ptr - fmt;
! 237:
! 238: if (format_replace(ft, fmt, n, &buf, &len, &off) != 0)
! 239: break;
! 240: fmt += n + 1;
! 241: continue;
! 242: default:
! 243: if (ch >= 'A' && ch <= 'Z') {
! 244: s = format_aliases[ch - 'A'];
! 245: if (s != NULL) {
! 246: n = strlen(s);
! 247: if (format_replace (
! 248: ft, s, n, &buf, &len, &off) != 0)
! 249: break;
! 250: continue;
! 251: }
! 252: }
! 253: while (len - off < 2) {
! 254: buf = xrealloc(buf, 2, len);
! 255: len *= 2;
! 256: }
! 257: buf[off++] = ch;
! 258: continue;
! 259: }
! 260:
! 261: break;
! 262: }
! 263: buf[off] = '\0';
! 264:
! 265: return (buf);
! 266: }
! 267:
! 268: /* Set default format keys for a session. */
! 269: void
! 270: format_session(struct format_tree *ft, struct session *s)
! 271: {
! 272: struct session_group *sg;
! 273: char *tim;
! 274: time_t t;
! 275:
! 276: format_add(ft, "session_name", "%s", s->name);
! 277: format_add(ft, "session_windows", "%u", winlink_count(&s->windows));
! 278: format_add(ft, "session_width", "%u", s->sx);
! 279: format_add(ft, "session_height", "%u", s->sy);
! 280:
! 281: sg = session_group_find(s);
! 282: format_add(ft, "session_grouped", "%d", sg != NULL);
! 283: if (sg != NULL)
! 284: format_add(ft, "session_group", "%u", session_group_index(sg));
! 285:
! 286: t = s->creation_time.tv_sec;
! 287: format_add(ft, "session_created", "%ld", (long) t);
! 288: tim = ctime(&t);
! 289: *strchr(tim, '\n') = '\0';
! 290: format_add(ft, "session_created_string", "%s", tim);
! 291:
! 292: if (s->flags & SESSION_UNATTACHED)
! 293: format_add(ft, "session_attached", "%d", 0);
! 294: else
! 295: format_add(ft, "session_attached", "%d", 1);
! 296: }
! 297:
! 298: /* Set default format keys for a winlink. */
! 299: void
! 300: format_winlink(struct format_tree *ft, struct session *s, struct winlink *wl)
! 301: {
! 302: struct window *w = wl->window;
! 303: char *layout, *flags;
! 304:
! 305: layout = layout_dump(w);
! 306: flags = window_printable_flags(s, wl);
! 307:
! 308: format_add(ft, "window_index", "%d", wl->idx);
! 309: format_add(ft, "window_name", "%s", w->name);
! 310: format_add(ft, "window_width", "%u", w->sx);
! 311: format_add(ft, "window_height", "%u", w->sy);
! 312: format_add(ft, "window_flags", "%s", flags);
! 313: format_add(ft, "window_layout", "%s", layout);
! 314: format_add(ft, "window_active", "%d", wl == s->curw);
! 315:
! 316: xfree(flags);
! 317: xfree(layout);
! 318: }
! 319:
! 320: /* Set default format keys for a window pane. */
! 321: void
! 322: format_window_pane(struct format_tree *ft, struct window_pane *wp)
! 323: {
! 324: struct grid *gd = wp->base.grid;
! 325: struct grid_line *gl;
! 326: unsigned long long size;
! 327: u_int i;
! 328:
! 329: size = 0;
! 330: for (i = 0; i < gd->hsize; i++) {
! 331: gl = &gd->linedata[i];
! 332: size += gl->cellsize * sizeof *gl->celldata;
! 333: size += gl->utf8size * sizeof *gl->utf8data;
! 334: }
! 335: size += gd->hsize * sizeof *gd->linedata;
! 336:
! 337: format_add(ft, "pane_width", "%u", wp->sx);
! 338: format_add(ft, "pane_height", "%u", wp->sy);
! 339: format_add(ft, "pane_title", "%s", wp->base.title);
! 340: format_add(ft, "history_size", "%u", gd->hsize);
! 341: format_add(ft, "history_limit", "%u", gd->hlimit);
! 342: format_add(ft, "history_bytes", "%llu", size);
! 343: format_add(ft, "pane_id", "%%%u", wp->id);
! 344: format_add(ft, "pane_active", "%d", wp == wp->window->active);
! 345: format_add(ft, "pane_dead", "%d", wp->fd == -1);
! 346: }