/*************************************************************************
* TinyFugue - programmable mud client
* Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2002, 2003, 2004, 2005, 2006-2007 Ken Keys
*
* TinyFugue (aka "tf") is protected under the terms of the GNU
* General Public License. See the file "COPYING" for details.
************************************************************************/
static const char RCSid[] = "$Id: attr.c,v 35004.10 2007/01/13 23:12:39 kkeys Exp $";
#include "tfconfig.h"
#include "port.h"
#include "tf.h"
#include "util.h"
#include "pattern.h" /* for tfio.h */
#include "search.h"
#include "tfio.h"
#include "output.h"
#include "attr.h"
#include "variable.h"
#include "parse.h" /* valstd() */
const int feature_256colors = (NCOLORS == 256);
/* gagged labels will be elided in the "valid values" list of enum2int() */
conString enum_color[]= {
STRING_LITERAL("black"),
STRING_LITERAL("red"),
STRING_LITERAL("green"),
STRING_LITERAL("yellow"),
STRING_LITERAL("blue"),
STRING_LITERAL("magenta"),
STRING_LITERAL("cyan"),
STRING_LITERAL("white"),
STRING_LITERAL("gray"),
STRING_LITERAL("brightred"),
STRING_LITERAL_ATTR("brightgreen", F_GAG),
STRING_LITERAL_ATTR("brightyellow", F_GAG),
STRING_LITERAL_ATTR("brightblue", F_GAG),
STRING_LITERAL_ATTR("brightmagenta", F_GAG),
STRING_LITERAL_ATTR("brightcyan", F_GAG),
STRING_LITERAL("brightwhite"),
#if NCOLORS == 256
STRING_LITERAL("rgb000"),
STRING_LITERAL_ATTR("rgb001", F_GAG),
STRING_LITERAL_ATTR("rgb002", F_GAG),
STRING_LITERAL_ATTR("rgb003", F_GAG),
STRING_LITERAL_ATTR("rgb004", F_GAG),
STRING_LITERAL_ATTR("rgb005", F_GAG),
STRING_LITERAL_ATTR("rgb010", F_GAG),
STRING_LITERAL_ATTR("rgb011", F_GAG),
STRING_LITERAL_ATTR("rgb012", F_GAG),
STRING_LITERAL_ATTR("rgb013", F_GAG),
STRING_LITERAL_ATTR("rgb014", F_GAG),
STRING_LITERAL_ATTR("rgb015", F_GAG),
STRING_LITERAL_ATTR("rgb020", F_GAG),
STRING_LITERAL_ATTR("rgb021", F_GAG),
STRING_LITERAL_ATTR("rgb022", F_GAG),
STRING_LITERAL_ATTR("rgb023", F_GAG),
STRING_LITERAL_ATTR("rgb024", F_GAG),
STRING_LITERAL_ATTR("rgb025", F_GAG),
STRING_LITERAL_ATTR("rgb030", F_GAG),
STRING_LITERAL_ATTR("rgb031", F_GAG),
STRING_LITERAL_ATTR("rgb032", F_GAG),
STRING_LITERAL_ATTR("rgb033", F_GAG),
STRING_LITERAL_ATTR("rgb034", F_GAG),
STRING_LITERAL_ATTR("rgb035", F_GAG),
STRING_LITERAL_ATTR("rgb040", F_GAG),
STRING_LITERAL_ATTR("rgb041", F_GAG),
STRING_LITERAL_ATTR("rgb042", F_GAG),
STRING_LITERAL_ATTR("rgb043", F_GAG),
STRING_LITERAL_ATTR("rgb044", F_GAG),
STRING_LITERAL_ATTR("rgb045", F_GAG),
STRING_LITERAL_ATTR("rgb050", F_GAG),
STRING_LITERAL_ATTR("rgb051", F_GAG),
STRING_LITERAL_ATTR("rgb052", F_GAG),
STRING_LITERAL_ATTR("rgb053", F_GAG),
STRING_LITERAL_ATTR("rgb054", F_GAG),
STRING_LITERAL_ATTR("rgb055", F_GAG),
STRING_LITERAL_ATTR("rgb100", F_GAG),
STRING_LITERAL_ATTR("rgb101", F_GAG),
STRING_LITERAL_ATTR("rgb102", F_GAG),
STRING_LITERAL_ATTR("rgb103", F_GAG),
STRING_LITERAL_ATTR("rgb104", F_GAG),
STRING_LITERAL_ATTR("rgb105", F_GAG),
STRING_LITERAL_ATTR("rgb110", F_GAG),
STRING_LITERAL_ATTR("rgb111", F_GAG),
STRING_LITERAL_ATTR("rgb112", F_GAG),
STRING_LITERAL_ATTR("rgb113", F_GAG),
STRING_LITERAL_ATTR("rgb114", F_GAG),
STRING_LITERAL_ATTR("rgb115", F_GAG),
STRING_LITERAL_ATTR("rgb120", F_GAG),
STRING_LITERAL_ATTR("rgb121", F_GAG),
STRING_LITERAL_ATTR("rgb122", F_GAG),
STRING_LITERAL_ATTR("rgb123", F_GAG),
STRING_LITERAL_ATTR("rgb124", F_GAG),
STRING_LITERAL_ATTR("rgb125", F_GAG),
STRING_LITERAL_ATTR("rgb130", F_GAG),
STRING_LITERAL_ATTR("rgb131", F_GAG),
STRING_LITERAL_ATTR("rgb132", F_GAG),
STRING_LITERAL_ATTR("rgb133", F_GAG),
STRING_LITERAL_ATTR("rgb134", F_GAG),
STRING_LITERAL_ATTR("rgb135", F_GAG),
STRING_LITERAL_ATTR("rgb140", F_GAG),
STRING_LITERAL_ATTR("rgb141", F_GAG),
STRING_LITERAL_ATTR("rgb142", F_GAG),
STRING_LITERAL_ATTR("rgb143", F_GAG),
STRING_LITERAL_ATTR("rgb144", F_GAG),
STRING_LITERAL_ATTR("rgb145", F_GAG),
STRING_LITERAL_ATTR("rgb150", F_GAG),
STRING_LITERAL_ATTR("rgb151", F_GAG),
STRING_LITERAL_ATTR("rgb152", F_GAG),
STRING_LITERAL_ATTR("rgb153", F_GAG),
STRING_LITERAL_ATTR("rgb154", F_GAG),
STRING_LITERAL_ATTR("rgb155", F_GAG),
STRING_LITERAL_ATTR("rgb200", F_GAG),
STRING_LITERAL_ATTR("rgb201", F_GAG),
STRING_LITERAL_ATTR("rgb202", F_GAG),
STRING_LITERAL_ATTR("rgb203", F_GAG),
STRING_LITERAL_ATTR("rgb204", F_GAG),
STRING_LITERAL_ATTR("rgb205", F_GAG),
STRING_LITERAL_ATTR("rgb210", F_GAG),
STRING_LITERAL_ATTR("rgb211", F_GAG),
STRING_LITERAL_ATTR("rgb212", F_GAG),
STRING_LITERAL_ATTR("rgb213", F_GAG),
STRING_LITERAL_ATTR("rgb214", F_GAG),
STRING_LITERAL_ATTR("rgb215", F_GAG),
STRING_LITERAL_ATTR("rgb220", F_GAG),
STRING_LITERAL_ATTR("rgb221", F_GAG),
STRING_LITERAL_ATTR("rgb222", F_GAG),
STRING_LITERAL_ATTR("rgb223", F_GAG),
STRING_LITERAL_ATTR("rgb224", F_GAG),
STRING_LITERAL_ATTR("rgb225", F_GAG),
STRING_LITERAL_ATTR("rgb230", F_GAG),
STRING_LITERAL_ATTR("rgb231", F_GAG),
STRING_LITERAL_ATTR("rgb232", F_GAG),
STRING_LITERAL_ATTR("rgb233", F_GAG),
STRING_LITERAL_ATTR("rgb234", F_GAG),
STRING_LITERAL_ATTR("rgb235", F_GAG),
STRING_LITERAL_ATTR("rgb240", F_GAG),
STRING_LITERAL_ATTR("rgb241", F_GAG),
STRING_LITERAL_ATTR("rgb242", F_GAG),
STRING_LITERAL_ATTR("rgb243", F_GAG),
STRING_LITERAL_ATTR("rgb244", F_GAG),
STRING_LITERAL_ATTR("rgb245", F_GAG),
STRING_LITERAL_ATTR("rgb250", F_GAG),
STRING_LITERAL_ATTR("rgb251", F_GAG),
STRING_LITERAL_ATTR("rgb252", F_GAG),
STRING_LITERAL_ATTR("rgb253", F_GAG),
STRING_LITERAL_ATTR("rgb254", F_GAG),
STRING_LITERAL_ATTR("rgb255", F_GAG),
STRING_LITERAL_ATTR("rgb300", F_GAG),
STRING_LITERAL_ATTR("rgb301", F_GAG),
STRING_LITERAL_ATTR("rgb302", F_GAG),
STRING_LITERAL_ATTR("rgb303", F_GAG),
STRING_LITERAL_ATTR("rgb304", F_GAG),
STRING_LITERAL_ATTR("rgb305", F_GAG),
STRING_LITERAL_ATTR("rgb310", F_GAG),
STRING_LITERAL_ATTR("rgb311", F_GAG),
STRING_LITERAL_ATTR("rgb312", F_GAG),
STRING_LITERAL_ATTR("rgb313", F_GAG),
STRING_LITERAL_ATTR("rgb314", F_GAG),
STRING_LITERAL_ATTR("rgb315", F_GAG),
STRING_LITERAL_ATTR("rgb320", F_GAG),
STRING_LITERAL_ATTR("rgb321", F_GAG),
STRING_LITERAL_ATTR("rgb322", F_GAG),
STRING_LITERAL_ATTR("rgb323", F_GAG),
STRING_LITERAL_ATTR("rgb324", F_GAG),
STRING_LITERAL_ATTR("rgb325", F_GAG),
STRING_LITERAL_ATTR("rgb330", F_GAG),
STRING_LITERAL_ATTR("rgb331", F_GAG),
STRING_LITERAL_ATTR("rgb332", F_GAG),
STRING_LITERAL_ATTR("rgb333", F_GAG),
STRING_LITERAL_ATTR("rgb334", F_GAG),
STRING_LITERAL_ATTR("rgb335", F_GAG),
STRING_LITERAL_ATTR("rgb340", F_GAG),
STRING_LITERAL_ATTR("rgb341", F_GAG),
STRING_LITERAL_ATTR("rgb342", F_GAG),
STRING_LITERAL_ATTR("rgb343", F_GAG),
STRING_LITERAL_ATTR("rgb344", F_GAG),
STRING_LITERAL_ATTR("rgb345", F_GAG),
STRING_LITERAL_ATTR("rgb350", F_GAG),
STRING_LITERAL_ATTR("rgb351", F_GAG),
STRING_LITERAL_ATTR("rgb352", F_GAG),
STRING_LITERAL_ATTR("rgb353", F_GAG),
STRING_LITERAL_ATTR("rgb354", F_GAG),
STRING_LITERAL_ATTR("rgb355", F_GAG),
STRING_LITERAL_ATTR("rgb400", F_GAG),
STRING_LITERAL_ATTR("rgb401", F_GAG),
STRING_LITERAL_ATTR("rgb402", F_GAG),
STRING_LITERAL_ATTR("rgb403", F_GAG),
STRING_LITERAL_ATTR("rgb404", F_GAG),
STRING_LITERAL_ATTR("rgb405", F_GAG),
STRING_LITERAL_ATTR("rgb410", F_GAG),
STRING_LITERAL_ATTR("rgb411", F_GAG),
STRING_LITERAL_ATTR("rgb412", F_GAG),
STRING_LITERAL_ATTR("rgb413", F_GAG),
STRING_LITERAL_ATTR("rgb414", F_GAG),
STRING_LITERAL_ATTR("rgb415", F_GAG),
STRING_LITERAL_ATTR("rgb420", F_GAG),
STRING_LITERAL_ATTR("rgb421", F_GAG),
STRING_LITERAL_ATTR("rgb422", F_GAG),
STRING_LITERAL_ATTR("rgb423", F_GAG),
STRING_LITERAL_ATTR("rgb424", F_GAG),
STRING_LITERAL_ATTR("rgb425", F_GAG),
STRING_LITERAL_ATTR("rgb430", F_GAG),
STRING_LITERAL_ATTR("rgb431", F_GAG),
STRING_LITERAL_ATTR("rgb432", F_GAG),
STRING_LITERAL_ATTR("rgb433", F_GAG),
STRING_LITERAL_ATTR("rgb434", F_GAG),
STRING_LITERAL_ATTR("rgb435", F_GAG),
STRING_LITERAL_ATTR("rgb440", F_GAG),
STRING_LITERAL_ATTR("rgb441", F_GAG),
STRING_LITERAL_ATTR("rgb442", F_GAG),
STRING_LITERAL_ATTR("rgb443", F_GAG),
STRING_LITERAL_ATTR("rgb444", F_GAG),
STRING_LITERAL_ATTR("rgb445", F_GAG),
STRING_LITERAL_ATTR("rgb450", F_GAG),
STRING_LITERAL_ATTR("rgb451", F_GAG),
STRING_LITERAL_ATTR("rgb452", F_GAG),
STRING_LITERAL_ATTR("rgb453", F_GAG),
STRING_LITERAL_ATTR("rgb454", F_GAG),
STRING_LITERAL_ATTR("rgb455", F_GAG),
STRING_LITERAL_ATTR("rgb500", F_GAG),
STRING_LITERAL_ATTR("rgb501", F_GAG),
STRING_LITERAL_ATTR("rgb502", F_GAG),
STRING_LITERAL_ATTR("rgb503", F_GAG),
STRING_LITERAL_ATTR("rgb504", F_GAG),
STRING_LITERAL_ATTR("rgb505", F_GAG),
STRING_LITERAL_ATTR("rgb510", F_GAG),
STRING_LITERAL_ATTR("rgb511", F_GAG),
STRING_LITERAL_ATTR("rgb512", F_GAG),
STRING_LITERAL_ATTR("rgb513", F_GAG),
STRING_LITERAL_ATTR("rgb514", F_GAG),
STRING_LITERAL_ATTR("rgb515", F_GAG),
STRING_LITERAL_ATTR("rgb520", F_GAG),
STRING_LITERAL_ATTR("rgb521", F_GAG),
STRING_LITERAL_ATTR("rgb522", F_GAG),
STRING_LITERAL_ATTR("rgb523", F_GAG),
STRING_LITERAL_ATTR("rgb524", F_GAG),
STRING_LITERAL_ATTR("rgb525", F_GAG),
STRING_LITERAL_ATTR("rgb530", F_GAG),
STRING_LITERAL_ATTR("rgb531", F_GAG),
STRING_LITERAL_ATTR("rgb532", F_GAG),
STRING_LITERAL_ATTR("rgb533", F_GAG),
STRING_LITERAL_ATTR("rgb534", F_GAG),
STRING_LITERAL_ATTR("rgb535", F_GAG),
STRING_LITERAL_ATTR("rgb540", F_GAG),
STRING_LITERAL_ATTR("rgb541", F_GAG),
STRING_LITERAL_ATTR("rgb542", F_GAG),
STRING_LITERAL_ATTR("rgb543", F_GAG),
STRING_LITERAL_ATTR("rgb544", F_GAG),
STRING_LITERAL_ATTR("rgb545", F_GAG),
STRING_LITERAL_ATTR("rgb550", F_GAG),
STRING_LITERAL_ATTR("rgb551", F_GAG),
STRING_LITERAL_ATTR("rgb552", F_GAG),
STRING_LITERAL_ATTR("rgb553", F_GAG),
STRING_LITERAL_ATTR("rgb554", F_GAG),
STRING_LITERAL("rgb555"),
STRING_LITERAL("gray0"),
STRING_LITERAL_ATTR("gray1", F_GAG),
STRING_LITERAL_ATTR("gray2", F_GAG),
STRING_LITERAL_ATTR("gray3", F_GAG),
STRING_LITERAL_ATTR("gray4", F_GAG),
STRING_LITERAL_ATTR("gray5", F_GAG),
STRING_LITERAL_ATTR("gray6", F_GAG),
STRING_LITERAL_ATTR("gray7", F_GAG),
STRING_LITERAL_ATTR("gray8", F_GAG),
STRING_LITERAL_ATTR("gray9", F_GAG),
STRING_LITERAL_ATTR("gray10", F_GAG),
STRING_LITERAL_ATTR("gray11", F_GAG),
STRING_LITERAL_ATTR("gray12", F_GAG),
STRING_LITERAL_ATTR("gray13", F_GAG),
STRING_LITERAL_ATTR("gray14", F_GAG),
STRING_LITERAL_ATTR("gray15", F_GAG),
STRING_LITERAL_ATTR("gray16", F_GAG),
STRING_LITERAL_ATTR("gray17", F_GAG),
STRING_LITERAL_ATTR("gray18", F_GAG),
STRING_LITERAL_ATTR("gray19", F_GAG),
STRING_LITERAL_ATTR("gray20", F_GAG),
STRING_LITERAL_ATTR("gray21", F_GAG),
STRING_LITERAL_ATTR("gray22", F_GAG),
STRING_LITERAL("gray23"),
#endif
STRING_NULL };
void init_attrs(void)
{
Var *var;
for (var = special_var; var->val.name; var++) {
if (var->func == ch_attr)
ch_attr(var);
}
}
/* Return the result of combining adj into base. If adj has the 'x' attr,
* discard base. If adj has any colors, they override colors in base. Other
* attrs are OR'd.
*/
attr_t adj_attr(attr_t base, attr_t adj)
{
/* XXX BUG: User should be able to change hiliteattr, do a /recall, and
* see old hilited lines displayed with the new hiliteattr. Interpreting
* F_HILITE here breaks that feature. But the interpretation is necessary
* to make colors override correctly when hiliteattr includes a color.
*/
if (base & F_HILITE) {
base &= ~F_HILITE;
base |= hiliteattr;
}
if (adj & F_HILITE) {
adj &= ~F_HILITE;
adj |= hiliteattr;
}
if (adj & F_EXCLUSIVE)
return adj;
if (base & adj & F_FGCOLOR)
base &= ~F_FGCOLORMASK;
if (base & adj & F_BGCOLOR)
base &= ~F_BGCOLORMASK;
return base | adj;
}
/* convert attr string to bitfields */
const char *parse_attrs(const char *str, attr_t *attrp, int delimiter)
{
int color, len;
const char *name;
char buf[16];
char reject[3] = {',', '\0', '\0'};
reject[1] = delimiter;
*attrp = 0;
if (!str) return "";
while (*str && *str != delimiter) {
++str;
switch(str[-1]) {
case ',': /* skip */ break;
case 'n': *attrp |= F_NONE; break;
case 'x': *attrp |= F_EXCLUSIVE; break;
case 'G': *attrp |= F_NOHISTORY; break;
case 'L': *attrp |= F_NOLOG; break;
case 'A': *attrp |= F_NOACTIVITY;break;
case 'g': *attrp |= F_GAG; break;
case 'u': *attrp |= F_UNDERLINE; break;
case 'r': *attrp |= F_REVERSE; break;
case 'f': *attrp |= F_FLASH; break;
case 'd': *attrp |= F_DIM; break;
case 'B': *attrp |= F_BOLD; break;
case 'b': *attrp |= F_BELL; break;
case 'h': *attrp |= F_HILITE; break;
case 'E': *attrp |= error_attr; break;
case 'W': *attrp |= warning_attr; break;
case 'I': *attrp |= info_attr; break;
case 'C':
len = strcspn(str, reject);
if (str[len] && len < sizeof(buf)) {
name = strncpy(buf, str, len);
buf[len] = '\0';
str += len;
} else {
name = str;
while (*str) ++str;
}
if (strncmp(name, "bg", 2) == 0) {
if ((color = enum2int(name+2, 0, enum_color, "bgcolor")) < 0)
return NULL;
*attrp = adj_attr(*attrp, bgcolor2attr(color));
} else {
if ((color = enum2int(name, 0, enum_color, "color")) < 0)
return NULL;
*attrp = adj_attr(*attrp, fgcolor2attr(color));
}
break;
default:
eprintf("invalid display attribute '%c'", str[-1]);
return NULL;
}
}
return str;
}
int ch_status_attr(Var *var)
{
if (!ch_attr(var)) return 0;
update_status_line(NULL);
return 1;
}
int ch_attr(Var *var)
{
Value *val;
const char *str;
attr_t attr;
if (var->val.type & ~(TYPE_STR|TYPE_ATTR)) {
/* extremely unlikely, so we don't bother to deal with it correctly,
* just do enough that we don't crash. */
internal_error(__FILE__, __LINE__,
"ch_attr: variable '%s' has type %04x",
var->val.name, var->val.type);
return 0;
}
if (!(val = getvarval(var)) || !(str = valstd(val))) {
var->val.u.attr = 0;
} else if (parse_attrs(str, &attr, 0)) {
var->val.u.attr = attr;
} else {
var->val.type &= ~TYPE_ATTR;
return 0;
}
var->val.type |= TYPE_ATTR;
return 1;
}
static void set_attr(String *line, int offset, attr_t *starting,
attr_t current)
{
/* starting_attrs is set by the attrs parameter and/or codes at the
* beginning of the line. If no visible mid-line changes occur, there is
* no need to allocate line->charattrs (which would increase the size of
* the line by ~5x). Note that a trailing attribute change is considered
* a mid-line change; this is sub-optimal, but unprompt() depends on it
* (it expects prompt->attrs to be the original starting attributes).
*/
if (!line->charattrs) {
if (line->len == 0) {
/* start of visible line */
*starting = current;
} else if (*starting != current) {
/* First mid-line attr change. */
check_charattrs(line, line->len, *starting,
__FILE__, __LINE__);
}
}
if (line->charattrs)
while (offset < line->len)
line->charattrs[offset++] = current;
}
#define ANSI_CSI (char)0233 /* ANSI terminal Command Sequence Intro */
/* Interpret embedded codes from a subset of ansi codes:
* ansi attribute/color codes are converted to tf character or line attrs;
* tabs are expanded (if %expand_tabs is on); all other codes are ignored.
* (EMUL_DEBUG was handled in handle_socket_input())
*/
String *decode_ansi(const char *s, attr_t attrs, int emul, attr_t *final_attrs)
{
String *dst;
int i, colorstate = 0;
attr_t starting_attrs = attrs;
if (emul == EMUL_RAW || emul == EMUL_DEBUG) {
if (final_attrs) *final_attrs = attrs;
return Stringnew(s, -1, attrs);
}
dst = Stringnew(NULL, 1, 0);
for ( ; *s; s++) {
if ((emul >= EMUL_ANSI_STRIP) &&
(*s == ANSI_CSI || (s[0] == '\033' && s[1] == '[' && s++)))
{
/* Start with current value of attrs, and collect attributes from
* ANSI codes. But we don't write to attrs directly, in case this
* turns out to not be an "m" command. */
attr_t new = attrs;
if (!*s) break; /* in case code got truncated */
do {
s++;
i = strtoint(s, &s);
if (colorstate < 0) {
/* ignoring everything after error */
} else if (colorstate % 10 == 8) {
if (i == 5) colorstate++;
else colorstate = -1; /* error */
} else if (colorstate % 10 == 9) {
if (i < 0 || i > 255)
colorstate = -1; /* error */
else {
#if NCOLORS == 256
if (colorstate / 10 == 3)
new = (new & ~F_FGCOLORMASK) | fgcolor2attr(i);
else
new = (new & ~F_BGCOLORMASK) | bgcolor2attr(i);
#endif
colorstate = 0;
}
} else if (!i || emul < EMUL_ANSI_ATTR) {
new = 0;
} else if (i >= 30 && i <= 37) {
new = (new & ~F_FGCOLORMASK) | fgcolor2attr(i - 30);
} else if (i >= 40 && i <= 47) {
new = (new & ~F_BGCOLORMASK) | bgcolor2attr(i - 40);
} else if (i >= 90 && i <= 97) { /* not really ANSI */
new = (new & ~F_FGCOLORMASK) | fgcolor2attr(i - 90 + 8);
} else if (i >= 100 && i <= 107) { /* not really ANSI */
new = (new & ~F_BGCOLORMASK) | bgcolor2attr(i - 100 + 8);
} else if (i == 38 || i == 48) { /* not really ANSI */
colorstate = i;
/* Subsequences of the form "38;5;N" or "48;5;N" describe
* fg or bg xterm 256-color controls, where 0<=N<=255.
* Once we see the start of such a subsequence, any
* deviation from the correct form invalidates the rest
* of the control sequence.
*/
} else switch (i) {
case 1: new |= F_BOLD; break;
case 4: new |= F_UNDERLINE; break;
case 5: new |= F_FLASH; break;
case 7: new |= F_REVERSE; break;
case 21: new &= ~F_BOLD; break;
case 22: new &= ~(F_BOLD|F_DIM); break;
case 24: new &= ~F_UNDERLINE; break;
case 25: new &= ~F_FLASH; break;
case 27: new &= ~F_REVERSE; break;
default: /* ignore it */ break;
}
} while (s[0] == ';' && s[1]);
if (!*s) { /* in case code got truncated */
break;
} else if (*s == '?') { /* ignore ESC [ ? alnum */
if (!*++s) break;
} else if (*s == 'm') { /* attribute command */
attrs = new;
} /* ignore any other CSI command */
} else if ((emul >= EMUL_ANSI_STRIP) && (*s == '\033')) {
/* ignore ESC # digit, ESC ( alnum, ESC ) alnum, and ESC alnum. */
if (!*++s) break;
if (*s == '(' || *s == ')' || *s == '#')
if (!*++s) break;
} else if (is_print(*s) || *s == '\t') {
int orig_len = dst->len;
if (*s == '\t' && expand_tabs) {
Stringnadd(dst, ' ', tabsize - dst->len % tabsize);
} else {
Stringadd(dst, *s);
}
set_attr(dst, orig_len, &starting_attrs, attrs);
} else if (*s == '\b') {
/* bug: doesn't handle expanded tabs */
if (dst->len > 0)
Stringtrunc(dst, dst->len - 1);
} else if (*s == '\07') {
dst->attrs |= F_BELL;
}
}
if (!dst->charattrs) {
/* No mid-line changes, so apply starting_attrs to entire line */
dst->attrs |= starting_attrs;
} else {
dst->charattrs[dst->len] = attrs;
}
if (final_attrs) *final_attrs = attrs;
return dst;
}
/* Convert embedded '@' codes to internal character or line attrs. */
String *decode_attr(const conString *src, attr_t attrs, int offset)
{
const char *s;
String *dst;
int off;
attr_t new;
attr_t starting_attrs;
attr_t orig_attrs;
const cattr_t *orig_charattrs = src->charattrs;
dst = Stringnew(NULL, src->len, 0);
if (!src->data) return dst;
starting_attrs = dst->attrs = adj_attr(src->attrs, attrs);
for (s = src->data + offset; *s; s++) {
if (s[0] == '@' && s[1] == '{') {
s+=2;
if ((off = (*s == '~'))) s++;
s = parse_attrs(s, &new, '}');
if (!s) goto decode_attr_error;
if (*s != '}') {
eprintf("unmatched @{");
goto decode_attr_error;
}
if (new & F_BELL && !off) dst->attrs |= F_BELL;
new &= ~F_BELL;
if (new & F_FGCOLOR) attrs &= ~F_FGCOLORMASK;
if (new & F_BGCOLOR) attrs &= ~F_BGCOLORMASK;
if (new & F_NONE) attrs = 0;
if (off) attrs &= ~new;
else attrs |= new;
} else {
orig_attrs = orig_charattrs ? orig_charattrs[s - src->data] : 0;
Stringadd(dst, *s);
set_attr(dst, dst->len - 1, &starting_attrs, adj_attr(orig_attrs, attrs));
if (s[0] == '@' && s[1] == '@')
s++;
}
}
if (!dst->charattrs) {
/* No mid-line changes, so apply starting_attrs to entire line */
dst->attrs |= starting_attrs;
} else {
dst->charattrs[dst->len] = attrs;
}
return dst;
decode_attr_error:
dst->links++;
Stringfree(dst);
return NULL;
}
/* appends string representation of attrs to buffer */
String *attr2str(String *buffer, attr_t attrs)
{
if (attrs & F_NONE) Stringadd(buffer, 'n');
if (attrs & F_EXCLUSIVE) Stringadd(buffer, 'x');
if (attrs & F_GAG) Stringadd(buffer, 'g');
if (attrs & F_NOHISTORY) Stringadd(buffer, 'G');
if (attrs & F_NOLOG) Stringadd(buffer, 'L');
if (attrs & F_NOACTIVITY) Stringadd(buffer, 'A');
if (attrs & F_UNDERLINE) Stringadd(buffer, 'u');
if (attrs & F_REVERSE) Stringadd(buffer, 'r');
if (attrs & F_FLASH) Stringadd(buffer, 'f');
if (attrs & F_DIM) Stringadd(buffer, 'd');
if (attrs & F_BOLD) Stringadd(buffer, 'B');
if (attrs & F_BELL) Stringadd(buffer, 'b');
if (attrs & F_HILITE) Stringadd(buffer, 'h');
if (attrs & F_FGCOLOR)
SStringcat(Stringadd(buffer, 'C'), &enum_color[attr2fgcolor(attrs)]);
if (attrs & F_BGCOLOR) {
if (attrs & F_FGCOLOR) Stringadd(buffer, ',');
SStringcat(Stringcat(buffer, "Cbg"), &enum_color[attr2bgcolor(attrs)]);
}
return buffer;
}
String *encode_attr(const conString *str, int offset)
{
attr_t oldattrs = 0, attrs;
int i;
String *new;
new = Stringnew(NULL, str->len+1, 0);
if (!str->charattrs) {
if (str->attrs)
Stringadd(attr2str(Stringcat(new, "@{"), str->attrs), '}');
for (i = offset; i < str->len; i++) {
Stringadd(new, str->data[i]);
if (str->data[i] == '@')
Stringadd(new, '@');
}
if (str->attrs)
Stringcat(new, "@{n}");
} else {
for (i = offset; i < str->len; i++) {
attrs = adj_attr(str->attrs, str->charattrs[i]);
if ((attrs ^ oldattrs) & F_HWRITE) {
if (!(attrs & F_HWRITE)) {
/* no attrs */
Stringcat(new, "@{n}");
} else if (((oldattrs & ~attrs) & F_ENCODE) == 0) {
/* new attrs can be added to old attrs */
attr_t added = attrs & ~(oldattrs & F_SIMPLE);
if (((attrs ^ oldattrs) & F_FGCOLORS) == 0) /* fg same? */
added &= ~F_FGCOLORS; /* skip fg */
if (((attrs ^ oldattrs) & F_BGCOLORS) == 0) /* bg same? */
added &= ~F_BGCOLORS; /* skip bg */
Stringadd(attr2str(Stringcat(new, "@{"), added & F_HWRITE),
'}');
} else {
/* attrs are different */
Stringadd(attr2str(Stringcat(new, "@{n"), attrs & F_HWRITE),
'}');
}
}
Stringadd(new, str->data[i]);
if (str->data[i] == '@')
Stringadd(new, '@');
oldattrs = attrs;
}
if (attrs & F_HWRITE)
Stringcat(new, "@{n}");
}
return new;
}
static String *attr2ansi(String *str, attr_t attrs)
{
#define semi() if (str->len > orig_len) Stringadd(str,';')
int orig_len = str->len;
int color;
if (attrs & F_HILITE) attrs |= hiliteattr;
if (attrs & F_BOLD) { semi(); Stringcat(str, "1"); }
if (attrs & F_UNDERLINE) { semi(); Stringcat(str, "4"); }
if (attrs & F_FLASH) { semi(); Stringcat(str, "5"); }
if (attrs & F_REVERSE) { semi(); Stringcat(str, "7"); }
if (attrs & F_FGCOLOR) {
semi();
color = attr2fgcolor(attrs);
if (color < 8)
Sappendf(str, "%d", color + 30);
else if (color < 16) /* not really ANSI */
Sappendf(str, "%d", color - 8 + 90);
else /* not really ANSI */
Sappendf(str, "38;5;%d", color);
}
if (attrs & F_BGCOLOR) {
semi();
color = attr2bgcolor(attrs);
if (color < 8)
Sappendf(str, "%d", color + 40);
else if (color < 16) /* not really ANSI */
Sappendf(str, "%d", color - 8 + 100);
else /* not really ANSI */
Sappendf(str, "48;5;%d", color);
}
#undef semi
return str;
}
String *encode_ansi(const conString *str, int offset)
{
attr_t oldattrs = 0, attrs;
int i;
String *new;
new = Stringnew(NULL, str->len+1, 0);
if (!str->charattrs) {
if (str->attrs)
Stringadd(attr2ansi(Stringcat(new, "\033["), str->attrs), 'm');
Stringcat(new, str->data);
if (str->attrs)
Stringcat(new, "\033[m");
} else {
for (i = offset; i < str->len; i++) {
attrs = adj_attr(str->attrs, str->charattrs[i]);
if ((attrs ^ oldattrs) & F_HWRITE) {
if (!(attrs & F_HWRITE)) {
/* no attrs */
Stringcat(new, "\033[m");
} else if (((oldattrs & ~attrs) & F_ENCODE) == 0) {
/* new attrs can be added to old attrs */
attr_t added = attrs & ~(oldattrs & F_SIMPLE);
if (((attrs ^ oldattrs) & F_FGCOLORS) == 0) /* fg same? */
added &= ~F_FGCOLORS; /* skip fg */
if (((attrs ^ oldattrs) & F_BGCOLORS) == 0) /* bg same? */
added &= ~F_BGCOLORS; /* skip bg */
Stringadd(attr2ansi(Stringcat(new, "\033["),
added & F_HWRITE), 'm');
} else {
/* attrs are different */
Stringadd(attr2ansi(Stringcat(new, "\033[;"),
attrs & F_HWRITE), 'm');
}
}
Stringadd(new, str->data[i]);
oldattrs = attrs;
}
if (attrs & F_HWRITE)
Stringcat(new, "\033[m");
}
return new;
}