AmendHub

Download:

jcs

/

subtext

/

amendments

/

31

ansi: New variadic interface to generating ANSI strings

Callers can pass an unlimited number of ANSI attributes, terminating
the list with NULL.
 
Also add ansi_strip() which returns the length of the string with
ANSI escape codes removed, optionally storing the stripped string in
a newly allocated variable.

jcs made amendment 31 over 2 years ago
--- ansi.c Sat Dec 11 10:34:13 2021 +++ ansi.c Wed Dec 15 14:24:54 2021 @@ -14,22 +14,160 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include <stdarg.h> +#include <stdio.h> +#include <string.h> + #include "ansi.h" #include "session.h" +#include "util.h" +/* + * We may be outputting a bunch of ansi()s in a string, so they can't all + * return the same buffer. Rotate between a few on each iteration. + */ +static char ansi_buf[6][32]; +static short last_ansi_buf = 0; + char * -ansi(struct session *s, unsigned char attr) +ansi(struct session *s, ...) { - switch (attr) { - case ANSI_RESET: - if (s->ansi) - return "\33[0m"; - break; - case ANSI_BOLD: - if (s->ansi) - return "\33[1m"; - break; + char ansi_tmp[10]; + char *ansi_out; + va_list ap; + short attr, val, len; + + if (!s->ansi) + return ""; + + ansi_out = (char *)&ansi_buf[last_ansi_buf]; + if (++last_ansi_buf >= nitems(ansi_buf)) + last_ansi_buf = 0; + + *ansi_out = 0; + + va_start(ap, s); + while ((attr = va_arg(ap, short)) != 0) { + len = 0; + switch (attr) { + case ANSI_RESET: + len = strlcat(ansi_out, "\33[0m", sizeof(ansi_buf[0])); + break; + case ANSI_BOLD: + len = strlcat(ansi_out, "\33[1m", sizeof(ansi_buf[0])); + break; + case ANSI_UNDERLINE: + len = strlcat(ansi_out, "\33[4m", sizeof(ansi_buf[0])); + break; + case ANSI_REVERSE: + len = strlcat(ansi_out, "\33[7m", sizeof(ansi_buf[0])); + break; + + case ANSI_CLEAR_SCREEN: + len = strlcat(ansi_out, "\33[2J", sizeof(ansi_buf[0])); + break; + + case ANSI_ERASE_LINE: + /* XXX: this doesn't work on windows command-line telnet */ + len = strlcat(ansi_out, "\33[2K", sizeof(ansi_buf[0])); + break; + case ANSI_INSERT_LINE: + len = strlcat(ansi_out, "\33[L", sizeof(ansi_buf[0])); + break; + case ANSI_DELETE_LINE: + len = strlcat(ansi_out, "\33[M", sizeof(ansi_buf[0])); + break; + + case ANSI_BACKSPACE: + len = strlcat(ansi_out, "\33[D \33[D", sizeof(ansi_buf[0])); + break; + case ANSI_BACK: + len = strlcat(ansi_out, "\33[D", sizeof(ansi_buf[0])); + break; + case ANSI_UP: + len = strlcat(ansi_out, "\33[A", sizeof(ansi_buf[0])); + break; + case ANSI_DOWN: + len = strlcat(ansi_out, "\33[B", sizeof(ansi_buf[0])); + break; + + /* these require N args */ + case ANSI_BACK_N: + sprintf(ansi_tmp, "\33[%dB", va_arg(ap, short)); + len = strlcat(ansi_out, ansi_tmp, sizeof(ansi_buf[0])); + break; + case ANSI_CURSOR_N_N: + sprintf(ansi_tmp, "\33[%d;%dH", va_arg(ap, short), + va_arg(ap, short)); + len = strlcat(ansi_out, ansi_tmp, sizeof(ansi_buf[0])); + break; + case ANSI_COL_N: + /* \e[xG not supported on windows telnet, fake it */ + val = va_arg(ap, short); + if (val == 1) + len = strlcat(ansi_out, "\r", sizeof(ansi_buf[0])); + else { + sprintf(ansi_tmp, "\r\33[%dC", val); + len = strlcat(ansi_out, ansi_tmp, sizeof(ansi_buf[0])); + } + break; + case ANSI_ROW_N: + sprintf(ansi_tmp, "\33[%dH", va_arg(ap, short)); + len = strlcat(ansi_out, ansi_tmp, sizeof(ansi_buf[0])); + break; + + default: + panic("ansi: Unknown ANSI attribute %d", attr); + } + + if (len > sizeof(ansi_buf[0])) + panic("ansi: strlcat overflow; missing trailing NULL?"); } - return ""; + va_end(ap); + + return ansi_out; +} + +size_t +ansi_strip(char *inp, char **outp) +{ + char *ret; + size_t ilen = strlen(inp); + size_t n, olen = 0; + short in_csi; + + if (outp) + *outp = xmalloc(ilen + 1); + + for (n = 0, in_csi = 0; n < ilen; ) { + if (in_csi) { + /* eat "12;34;56" */ + while (n < ilen) { + if ((inp[n] >= '0' && inp[n] <= '9') || inp[n] == ';') + n++; + else + break; + } + + /* command character */ + if (n < ilen) + n++; + + in_csi = 0; + } else if (inp[n] == '\33' && inp[n + 1] == '[') { + in_csi = 1; + n += 2; + } else if (inp[n] == '\r') { + /* consider this an ansi character */ + n++; + } else { + if (outp) + (*outp)[olen] = inp[n]; + olen++; + n++; + } + } + + return olen; } --- ansi.h Sat Dec 11 10:37:36 2021 +++ ansi.h Wed Dec 15 14:06:45 2021 @@ -15,11 +15,35 @@ */ #ifndef __ANSI_H__ -#define __ANSI_H +#define __ANSI_H__ -#define ANSI_RESET 0 -#define ANSI_BOLD 1 +enum { + ANSI_RESET = 1, + ANSI_BOLD, + ANSI_UNDERLINE, + ANSI_REVERSE, + + ANSI_CLEAR_SCREEN, + + ANSI_ERASE_LINE, + ANSI_INSERT_LINE, + ANSI_DELETE_LINE, + + ANSI_BACKSPACE, + ANSI_BACK, + ANSI_UP, + ANSI_DOWN, + + /* these require N args */ + ANSI_BACK_N, + ANSI_CURSOR_N_N, + ANSI_COL_N, + ANSI_ROW_N, + + ANSI_NULL +}; -char *ansi(struct session *s, unsigned char attr); +char *ansi(struct session *s, ...); +size_t ansi_strip(char *inp, char **outp); #endif /* __ANSI_H__ */