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__ */