+ if(sel.ob.x == -1)
+ return NULL;
+
+ bufsize = (term.col+1) * (sel.ne.y-sel.nb.y+1) * UTF_SIZ;
+ ptr = str = xmalloc(bufsize);
+
+ /* append every set & selected glyph to the selection */
+ for(y = sel.nb.y; y <= sel.ne.y; y++) {
+ linelen = tlinelen(y);
+
+ if(sel.type == SEL_RECTANGULAR) {
+ gp = &term.line[y][sel.nb.x];
+ lastx = sel.ne.x;
+ } else {
+ gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0];
+ lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1;
+ }
+ last = &term.line[y][MIN(lastx, linelen-1)];
+ while(last >= gp && last->c[0] == ' ')
+ --last;
+
+ for( ; gp <= last; ++gp) {
+ if(gp->mode & ATTR_WDUMMY)
+ continue;
+
+ size = utf8len(gp->c);
+ memcpy(ptr, gp->c, size);
+ ptr += size;
+ }
+
+ /*
+ * Copy and pasting of line endings is inconsistent
+ * in the inconsistent terminal and GUI world.
+ * The best solution seems like to produce '\n' when
+ * something is copied from st and convert '\n' to
+ * '\r', when something to be pasted is received by
+ * st.
+ * FIXME: Fix the computer world.
+ */
+ if((y < sel.ne.y || lastx >= linelen) && !(last->mode & ATTR_WRAP))
+ *ptr++ = '\n';
+ }
+ *ptr = 0;
+ return str;
+}
+
+void
+selcopy(Time t) {
+ xsetsel(getsel(), t);
+}
+
+void
+selnotify(XEvent *e) {
+ ulong nitems, ofs, rem;
+ int format;
+ uchar *data, *last, *repl;
+ Atom type;
+ XSelectionEvent *xsev;
+
+ ofs = 0;
+ xsev = &e->xselection;
+ if (xsev->property == None)
+ return;
+ do {
+ if(XGetWindowProperty(xw.dpy, xw.win, xsev->property, ofs,
+ BUFSIZ/4, False, AnyPropertyType,
+ &type, &format, &nitems, &rem,
+ &data)) {
+ fprintf(stderr, "Clipboard allocation failed\n");
+ return;
+ }
+
+ /*
+ * As seen in getsel:
+ * Line endings are inconsistent in the terminal and GUI world
+ * copy and pasting. When receiving some selection data,
+ * replace all '\n' with '\r'.
+ * FIXME: Fix the computer world.
+ */
+ repl = data;
+ last = data + nitems * format / 8;
+ while((repl = memchr(repl, '\n', last - repl))) {
+ *repl++ = '\r';
+ }
+
+ if(IS_SET(MODE_BRCKTPASTE))
+ ttywrite("\033[200~", 6);
+ ttysend((char *)data, nitems * format / 8);
+ if(IS_SET(MODE_BRCKTPASTE))
+ ttywrite("\033[201~", 6);
+ XFree(data);
+ /* number of 32-bit chunks returned */
+ ofs += nitems * format / 32;
+ } while(rem > 0);
+}
+
+void
+selpaste(const Arg *dummy) {
+ XConvertSelection(xw.dpy, XA_PRIMARY, sel.xtarget, XA_PRIMARY,
+ xw.win, CurrentTime);
+}
+
+void
+clipcopy(const Arg *dummy) {
+ Atom clipboard;
+
+ if(sel.clipboard != NULL)
+ free(sel.clipboard);
+
+ if(sel.primary != NULL) {
+ sel.clipboard = xstrdup(sel.primary);
+ clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
+ XSetSelectionOwner(xw.dpy, clipboard, xw.win, CurrentTime);
+ }
+}
+
+void
+clippaste(const Arg *dummy) {
+ Atom clipboard;
+
+ clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
+ XConvertSelection(xw.dpy, clipboard, sel.xtarget, clipboard,
+ xw.win, CurrentTime);
+}
+
+void
+selclear(XEvent *e) {
+ if(sel.ob.x == -1)
+ return;
+ sel.ob.x = -1;
+ tsetdirt(sel.nb.y, sel.ne.y);
+}
+
+void
+selrequest(XEvent *e) {
+ XSelectionRequestEvent *xsre;
+ XSelectionEvent xev;
+ Atom xa_targets, string, clipboard;
+ char *seltext;
+
+ xsre = (XSelectionRequestEvent *) e;
+ xev.type = SelectionNotify;
+ xev.requestor = xsre->requestor;
+ xev.selection = xsre->selection;
+ xev.target = xsre->target;
+ xev.time = xsre->time;
+ if (xsre->property == None)
+ xsre->property = xsre->target;
+
+ /* reject */
+ xev.property = None;
+
+ xa_targets = XInternAtom(xw.dpy, "TARGETS", 0);
+ if(xsre->target == xa_targets) {
+ /* respond with the supported type */
+ string = sel.xtarget;
+ XChangeProperty(xsre->display, xsre->requestor, xsre->property,
+ XA_ATOM, 32, PropModeReplace,
+ (uchar *) &string, 1);
+ xev.property = xsre->property;
+ } else if(xsre->target == sel.xtarget || xsre->target == XA_STRING) {
+ /*
+ * xith XA_STRING non ascii characters may be incorrect in the
+ * requestor. It is not our problem, use utf8.
+ */
+ clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
+ if(xsre->selection == XA_PRIMARY) {
+ seltext = sel.primary;
+ } else if(xsre->selection == clipboard) {
+ seltext = sel.clipboard;
+ } else {
+ fprintf(stderr,
+ "Unhandled clipboard selection 0x%lx\n",
+ xsre->selection);
+ return;
+ }
+ if(seltext != NULL) {
+ XChangeProperty(xsre->display, xsre->requestor,
+ xsre->property, xsre->target,
+ 8, PropModeReplace,
+ (uchar *)seltext, strlen(seltext));
+ xev.property = xsre->property;
+ }
+ }
+
+ /* all done, send a notification to the listener */
+ if(!XSendEvent(xsre->display, xsre->requestor, True, 0, (XEvent *) &xev))
+ fprintf(stderr, "Error sending SelectionNotify event\n");
+}
+
+void
+xsetsel(char *str, Time t) {
+ free(sel.primary);
+ sel.primary = str;
+
+ XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t);
+ if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win)
+ selclear(0);
+}
+
+void
+brelease(XEvent *e) {
+ if(IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) {
+ mousereport(e);
+ return;
+ }
+
+ if(e->xbutton.button == Button2) {
+ selpaste(NULL);
+ } else if(e->xbutton.button == Button1) {
+ if(sel.mode < 2) {
+ selclear(NULL);
+ } else {
+ getbuttoninfo(e);
+ selcopy(e->xbutton.time);
+ }
+ sel.mode = 0;
+ tsetdirt(sel.nb.y, sel.ne.y);
+ }
+}
+
+void
+bmotion(XEvent *e) {
+ int oldey, oldex, oldsby, oldsey;
+
+ if(IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) {
+ mousereport(e);
+ return;
+ }
+
+ if(!sel.mode)
+ return;
+
+ sel.mode++;
+ oldey = sel.oe.y;
+ oldex = sel.oe.x;
+ oldsby = sel.nb.y;
+ oldsey = sel.ne.y;
+ getbuttoninfo(e);
+
+ if(oldey != sel.oe.y || oldex != sel.oe.x)
+ tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey));
+}
+
+void
+die(const char *errstr, ...) {
+ va_list ap;
+
+ va_start(ap, errstr);
+ vfprintf(stderr, errstr, ap);
+ va_end(ap);
+ exit(EXIT_FAILURE);
+}
+
+void
+execsh(void) {
+ char **args, *sh, *prog;
+ const struct passwd *pw;
+ char buf[sizeof(long) * 8 + 1];
+
+ errno = 0;
+ if((pw = getpwuid(getuid())) == NULL) {
+ if(errno)
+ die("getpwuid:%s\n", strerror(errno));
+ else
+ die("who are you?\n");
+ }
+
+ if (!(sh = getenv("SHELL"))) {
+ sh = (pw->pw_shell[0]) ? pw->pw_shell : shell;
+ }
+
+ if(opt_cmd)
+ prog = opt_cmd[0];
+ else if(utmp)
+ prog = utmp;
+ else
+ prog = sh;
+ args = (opt_cmd) ? opt_cmd : (char *[]) {prog, NULL};
+
+ snprintf(buf, sizeof(buf), "%lu", xw.win);
+
+ unsetenv("COLUMNS");
+ unsetenv("LINES");
+ unsetenv("TERMCAP");
+ setenv("LOGNAME", pw->pw_name, 1);
+ setenv("USER", pw->pw_name, 1);
+ setenv("SHELL", sh, 1);
+ setenv("HOME", pw->pw_dir, 1);
+ setenv("TERM", termname, 1);
+ setenv("WINDOWID", buf, 1);
+
+ signal(SIGCHLD, SIG_DFL);
+ signal(SIGHUP, SIG_DFL);
+ signal(SIGINT, SIG_DFL);
+ signal(SIGQUIT, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
+ signal(SIGALRM, SIG_DFL);
+
+ execvp(prog, args);
+ _exit(EXIT_FAILURE);
+}
+
+void
+sigchld(int a) {
+ int stat, ret;
+
+ if(waitpid(pid, &stat, 0) < 0)
+ die("Waiting for pid %hd failed: %s\n", pid, strerror(errno));
+
+ ret = WIFEXITED(stat) ? WEXITSTATUS(stat) : EXIT_FAILURE;
+ if (ret != EXIT_SUCCESS)
+ die("child finished with error '%d'\n", stat);
+ exit(EXIT_SUCCESS);
+}
+
+
+void
+stty(void)
+{
+ char cmd[_POSIX_ARG_MAX], **p, *q, *s;
+ size_t n, siz;
+
+ if((n = strlen(stty_args)) > sizeof(cmd)-1)
+ die("incorrect stty parameters\n");
+ memcpy(cmd, stty_args, n);
+ q = cmd + n;
+ siz = sizeof(cmd) - n;
+ for(p = opt_cmd; p && (s = *p); ++p) {
+ if((n = strlen(s)) > siz-1)
+ die("stty parameter length too long\n");
+ *q++ = ' ';
+ q = memcpy(q, s, n);
+ q += n;
+ siz-= n + 1;
+ }
+ *q = '\0';
+ system(cmd);
+}
+
+void
+ttynew(void) {
+ int m, s;
+ struct winsize w = {term.row, term.col, 0, 0};
+
+ if(opt_io) {
+ term.mode |= MODE_PRINT;
+ iofd = (!strcmp(opt_io, "-")) ?
+ STDOUT_FILENO :
+ open(opt_io, O_WRONLY | O_CREAT, 0666);
+ if(iofd < 0) {
+ fprintf(stderr, "Error opening %s:%s\n",
+ opt_io, strerror(errno));
+ }
+ }
+
+ if (opt_line) {
+ if((cmdfd = open(opt_line, O_RDWR)) < 0)
+ die("open line failed: %s\n", strerror(errno));
+ close(STDIN_FILENO);
+ dup(cmdfd);
+ stty();
+ return;
+ }
+
+ /* seems to work fine on linux, openbsd and freebsd */
+ if(openpty(&m, &s, NULL, NULL, &w) < 0)
+ die("openpty failed: %s\n", strerror(errno));
+
+ switch(pid = fork()) {
+ case -1:
+ die("fork failed\n");
+ break;
+ case 0:
+ close(iofd);
+ setsid(); /* create a new process group */
+ dup2(s, STDIN_FILENO);
+ dup2(s, STDOUT_FILENO);
+ dup2(s, STDERR_FILENO);
+ if(ioctl(s, TIOCSCTTY, NULL) < 0)
+ die("ioctl TIOCSCTTY failed: %s\n", strerror(errno));
+ close(s);
+ close(m);
+ execsh();
+ break;
+ default:
+ close(s);
+ cmdfd = m;
+ signal(SIGCHLD, sigchld);
+ break;
+ }
+}
+
+void
+ttyread(void) {
+ static char buf[BUFSIZ];
+ static int buflen = 0;
+ char *ptr;
+ char s[UTF_SIZ];
+ int charsize; /* size of utf8 char in bytes */
+ long unicodep;
+ int ret;
+
+ /* append read bytes to unprocessed bytes */
+ if((ret = read(cmdfd, buf+buflen, LEN(buf)-buflen)) < 0)
+ die("Couldn't read from shell: %s\n", strerror(errno));
+
+ /* process every complete utf8 char */
+ buflen += ret;
+ ptr = buf;
+ while((charsize = utf8decode(ptr, &unicodep, buflen))) {
+ utf8encode(unicodep, s, UTF_SIZ);
+ tputc(s, charsize);
+ ptr += charsize;
+ buflen -= charsize;
+ }
+
+ /* keep any uncomplete utf8 char for the next call */
+ memmove(buf, ptr, buflen);
+}
+
+void
+ttywrite(const char *s, size_t n) {
+ if(xwrite(cmdfd, s, n) == -1)
+ die("write error on tty: %s\n", strerror(errno));
+}
+
+void
+ttysend(char *s, size_t n) {
+ ttywrite(s, n);
+ if(IS_SET(MODE_ECHO))
+ techo(s, n);
+}
+
+void
+ttyresize(void) {
+ struct winsize w;
+
+ w.ws_row = term.row;
+ w.ws_col = term.col;
+ w.ws_xpixel = xw.tw;
+ w.ws_ypixel = xw.th;
+ if(ioctl(cmdfd, TIOCSWINSZ, &w) < 0)
+ fprintf(stderr, "Couldn't set window size: %s\n", strerror(errno));
+}
+
+int
+tattrset(int attr) {
+ int i, j;
+
+ for(i = 0; i < term.row-1; i++) {
+ for(j = 0; j < term.col-1; j++) {
+ if(term.line[i][j].mode & attr)
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+void
+tsetdirt(int top, int bot) {
+ int i;
+
+ LIMIT(top, 0, term.row-1);
+ LIMIT(bot, 0, term.row-1);
+
+ for(i = top; i <= bot; i++)
+ term.dirty[i] = 1;
+}
+
+void
+tsetdirtattr(int attr) {
+ int i, j;
+
+ for(i = 0; i < term.row-1; i++) {
+ for(j = 0; j < term.col-1; j++) {
+ if(term.line[i][j].mode & attr) {
+ tsetdirt(i, i);
+ break;
+ }
+ }
+ }
+}
+
+void
+tfulldirt(void) {
+ tsetdirt(0, term.row-1);
+}
+
+void
+tcursor(int mode) {
+ static TCursor c[2];
+ bool alt = IS_SET(MODE_ALTSCREEN);
+
+ if(mode == CURSOR_SAVE) {
+ c[alt] = term.c;
+ } else if(mode == CURSOR_LOAD) {
+ term.c = c[alt];
+ tmoveto(c[alt].x, c[alt].y);
+ }
+}
+
+void
+treset(void) {
+ uint i;
+
+ term.c = (TCursor){{
+ .mode = ATTR_NULL,
+ .fg = defaultfg,
+ .bg = defaultbg
+ }, .x = 0, .y = 0, .state = CURSOR_DEFAULT};
+
+ memset(term.tabs, 0, term.col * sizeof(*term.tabs));
+ for(i = tabspaces; i < term.col; i += tabspaces)
+ term.tabs[i] = 1;
+ term.top = 0;
+ term.bot = term.row - 1;
+ term.mode = MODE_WRAP;
+ memset(term.trantbl, sizeof(term.trantbl), CS_USA);
+ term.charset = 0;
+
+ for(i = 0; i < 2; i++) {
+ tmoveto(0, 0);
+ tcursor(CURSOR_SAVE);
+ tclearregion(0, 0, term.col-1, term.row-1);
+ tswapscreen();
+ }
+}
+
+void
+tnew(int col, int row) {
+ term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } };
+ tresize(col, row);
+ term.numlock = 1;
+
+ treset();
+}
+
+void
+tswapscreen(void) {
+ Line *tmp = term.line;
+
+ term.line = term.alt;
+ term.alt = tmp;
+ term.mode ^= MODE_ALTSCREEN;
+ tfulldirt();
+}
+
+void
+tscrolldown(int orig, int n) {
+ int i;
+ Line temp;
+
+ LIMIT(n, 0, term.bot-orig+1);
+
+ tsetdirt(orig, term.bot-n);
+ tclearregion(0, term.bot-n+1, term.col-1, term.bot);
+
+ for(i = term.bot; i >= orig+n; i--) {
+ temp = term.line[i];
+ term.line[i] = term.line[i-n];
+ term.line[i-n] = temp;
+ }
+
+ selscroll(orig, n);
+}
+
+void
+tscrollup(int orig, int n) {
+ int i;
+ Line temp;
+
+ LIMIT(n, 0, term.bot-orig+1);
+
+ tclearregion(0, orig, term.col-1, orig+n-1);
+ tsetdirt(orig+n, term.bot);
+
+ for(i = orig; i <= term.bot-n; i++) {
+ temp = term.line[i];
+ term.line[i] = term.line[i+n];
+ term.line[i+n] = temp;
+ }
+
+ selscroll(orig, -n);
+}
+
+void
+selscroll(int orig, int n) {
+ if(sel.ob.x == -1)
+ return;
+
+ if(BETWEEN(sel.ob.y, orig, term.bot) || BETWEEN(sel.oe.y, orig, term.bot)) {
+ if((sel.ob.y += n) > term.bot || (sel.oe.y += n) < term.top) {
+ selclear(NULL);
+ return;
+ }
+ if(sel.type == SEL_RECTANGULAR) {
+ if(sel.ob.y < term.top)
+ sel.ob.y = term.top;
+ if(sel.oe.y > term.bot)
+ sel.oe.y = term.bot;
+ } else {
+ if(sel.ob.y < term.top) {
+ sel.ob.y = term.top;
+ sel.ob.x = 0;
+ }
+ if(sel.oe.y > term.bot) {
+ sel.oe.y = term.bot;
+ sel.oe.x = term.col;
+ }
+ }
+ selnormalize();
+ }
+}
+
+void
+tnewline(int first_col) {
+ int y = term.c.y;
+
+ if(y == term.bot) {
+ tscrollup(term.top, 1);
+ } else {
+ y++;
+ }
+ tmoveto(first_col ? 0 : term.c.x, y);
+}
+
+void
+csiparse(void) {
+ char *p = csiescseq.buf, *np;
+ long int v;
+
+ csiescseq.narg = 0;
+ if(*p == '?') {
+ csiescseq.priv = 1;
+ p++;
+ }
+
+ csiescseq.buf[csiescseq.len] = '\0';
+ while(p < csiescseq.buf+csiescseq.len) {
+ np = NULL;
+ v = strtol(p, &np, 10);
+ if(np == p)
+ v = 0;
+ if(v == LONG_MAX || v == LONG_MIN)
+ v = -1;
+ csiescseq.arg[csiescseq.narg++] = v;
+ p = np;
+ if(*p != ';' || csiescseq.narg == ESC_ARG_SIZ)
+ break;
+ p++;
+ }
+ csiescseq.mode[0] = *p++;
+ csiescseq.mode[1] = (p < csiescseq.buf+csiescseq.len) ? *p : '\0';
+}
+
+/* for absolute user moves, when decom is set */
+void
+tmoveato(int x, int y) {
+ tmoveto(x, y + ((term.c.state & CURSOR_ORIGIN) ? term.top: 0));
+}
+
+void
+tmoveto(int x, int y) {
+ int miny, maxy;
+
+ if(term.c.state & CURSOR_ORIGIN) {
+ miny = term.top;
+ maxy = term.bot;
+ } else {
+ miny = 0;
+ maxy = term.row - 1;
+ }
+ term.c.state &= ~CURSOR_WRAPNEXT;
+ term.c.x = LIMIT(x, 0, term.col-1);
+ term.c.y = LIMIT(y, miny, maxy);
+}
+
+void
+tsetchar(char *c, Glyph *attr, int x, int y) {
+ static char *vt100_0[62] = { /* 0x41 - 0x7e */
+ "↑", "↓", "→", "←", "█", "▚", "☃", /* A - G */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* H - O */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* P - W */
+ 0, 0, 0, 0, 0, 0, 0, " ", /* X - _ */
+ "◆", "▒", "␉", "␌", "␍", "␊", "°", "±", /* ` - g */
+ "", "␋", "┘", "┐", "┌", "└", "┼", "⎺", /* h - o */
+ "⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */
+ "│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */
+ };
+
+ /*
+ * The table is proudly stolen from rxvt.
+ */
+ if(term.trantbl[term.charset] == CS_GRAPHIC0) {
+ if(BETWEEN(c[0], 0x41, 0x7e) && vt100_0[c[0] - 0x41]) {
+ c = vt100_0[c[0] - 0x41];
+ }
+ }
+
+ if(term.line[y][x].mode & ATTR_WIDE) {
+ if(x+1 < term.col) {
+ term.line[y][x+1].c[0] = ' ';
+ term.line[y][x+1].mode &= ~ATTR_WDUMMY;
+ }
+ } else if(term.line[y][x].mode & ATTR_WDUMMY) {
+ term.line[y][x-1].c[0] = ' ';
+ term.line[y][x-1].mode &= ~ATTR_WIDE;
+ }
+
+ term.dirty[y] = 1;
+ term.line[y][x] = *attr;
+ memcpy(term.line[y][x].c, c, UTF_SIZ);
+}
+
+void
+tclearregion(int x1, int y1, int x2, int y2) {
+ int x, y, temp;
+ Glyph *gp;
+
+ if(x1 > x2)
+ temp = x1, x1 = x2, x2 = temp;
+ if(y1 > y2)
+ temp = y1, y1 = y2, y2 = temp;
+
+ LIMIT(x1, 0, term.col-1);
+ LIMIT(x2, 0, term.col-1);
+ LIMIT(y1, 0, term.row-1);
+ LIMIT(y2, 0, term.row-1);
+
+ for(y = y1; y <= y2; y++) {
+ term.dirty[y] = 1;
+ for(x = x1; x <= x2; x++) {
+ gp = &term.line[y][x];
+ if(selected(x, y))
+ selclear(NULL);
+ gp->fg = term.c.attr.fg;
+ gp->bg = term.c.attr.bg;
+ gp->mode = 0;
+ memcpy(gp->c, " ", 2);
+ }
+ }
+}
+
+void
+tdeletechar(int n) {
+ int dst, src, size;
+ Glyph *line;
+
+ LIMIT(n, 0, term.col - term.c.x);
+
+ dst = term.c.x;
+ src = term.c.x + n;
+ size = term.col - src;
+ line = term.line[term.c.y];
+
+ memmove(&line[dst], &line[src], size * sizeof(Glyph));
+ tclearregion(term.col-n, term.c.y, term.col-1, term.c.y);
+}
+
+void
+tinsertblank(int n) {
+ int dst, src, size;
+ Glyph *line;
+
+ LIMIT(n, 0, term.col - term.c.x);
+
+ dst = term.c.x + n;
+ src = term.c.x;
+ size = term.col - dst;
+ line = term.line[term.c.y];
+
+ memmove(&line[dst], &line[src], size * sizeof(Glyph));
+ tclearregion(src, term.c.y, dst - 1, term.c.y);
+}
+
+void
+tinsertblankline(int n) {
+ if(BETWEEN(term.c.y, term.top, term.bot))
+ tscrolldown(term.c.y, n);
+}
+
+void
+tdeleteline(int n) {
+ if(BETWEEN(term.c.y, term.top, term.bot))
+ tscrollup(term.c.y, n);
+}
+
+int32_t
+tdefcolor(int *attr, int *npar, int l) {
+ int32_t idx = -1;
+ uint r, g, b;
+
+ switch (attr[*npar + 1]) {
+ case 2: /* direct color in RGB space */
+ if (*npar + 4 >= l) {
+ fprintf(stderr,
+ "erresc(38): Incorrect number of parameters (%d)\n",
+ *npar);
+ break;
+ }
+ r = attr[*npar + 2];
+ g = attr[*npar + 3];
+ b = attr[*npar + 4];
+ *npar += 4;
+ if(!BETWEEN(r, 0, 255) || !BETWEEN(g, 0, 255) || !BETWEEN(b, 0, 255))
+ fprintf(stderr, "erresc: bad rgb color (%d,%d,%d)\n",
+ r, g, b);
+ else
+ idx = TRUECOLOR(r, g, b);
+ break;
+ case 5: /* indexed color */
+ if (*npar + 2 >= l) {
+ fprintf(stderr,
+ "erresc(38): Incorrect number of parameters (%d)\n",
+ *npar);
+ break;
+ }
+ *npar += 2;
+ if(!BETWEEN(attr[*npar], 0, 255))
+ fprintf(stderr, "erresc: bad fgcolor %d\n", attr[*npar]);
+ else
+ idx = attr[*npar];
+ break;
+ case 0: /* implemented defined (only foreground) */
+ case 1: /* transparent */
+ case 3: /* direct color in CMY space */
+ case 4: /* direct color in CMYK space */
+ default:
+ fprintf(stderr,
+ "erresc(38): gfx attr %d unknown\n", attr[*npar]);
+ break;
+ }
+
+ return idx;
+}
+
+void
+tsetattr(int *attr, int l) {
+ int i;
+ int32_t idx;
+
+ for(i = 0; i < l; i++) {
+ switch(attr[i]) {
+ case 0:
+ term.c.attr.mode &= ~(
+ ATTR_BOLD |
+ ATTR_FAINT |
+ ATTR_ITALIC |
+ ATTR_UNDERLINE |
+ ATTR_BLINK |
+ ATTR_REVERSE |
+ ATTR_INVISIBLE |
+ ATTR_STRUCK );
+ term.c.attr.fg = defaultfg;
+ term.c.attr.bg = defaultbg;
+ break;