[master] 909ce71 Split the terminal emulation into its own source file.
Poul-Henning Kamp
phk at FreeBSD.org
Fri Jan 12 16:43:08 UTC 2018
commit 909ce71834084299f38ef8225c00392e0fd0c359
Author: Poul-Henning Kamp <phk at FreeBSD.org>
Date: Fri Jan 12 16:18:07 2018 +0000
Split the terminal emulation into its own source file.
Also: Make ansi-sequence problems fatal, and render unknown control
chars as '?'
diff --git a/bin/varnishtest/Makefile.am b/bin/varnishtest/Makefile.am
index 5ffbf79..974aaf9 100644
--- a/bin/varnishtest/Makefile.am
+++ b/bin/varnishtest/Makefile.am
@@ -48,6 +48,7 @@ varnishtest_SOURCES = \
vtc_proxy.c \
vtc_server.c \
vtc_subr.c \
+ vtc_term.c \
vtc_varnish.c
varnishtest_LDADD = \
diff --git a/bin/varnishtest/tests/u00000.vtc b/bin/varnishtest/tests/u00000.vtc
index ded88ab..0b33c36 100644
--- a/bin/varnishtest/tests/u00000.vtc
+++ b/bin/varnishtest/tests/u00000.vtc
@@ -28,6 +28,6 @@ process p4 -hexdump "cat" -start
process p4 -writeln "b\001z"
delay 0.5
process p4 -kill TERM
-process p4 -wait
+process p4 -wait -screen_dump
process p5 "stty -a ; sleep 1" -run -screen_dump
diff --git a/bin/varnishtest/vtc.h b/bin/varnishtest/vtc.h
index af89358..6efef45 100644
--- a/bin/varnishtest/vtc.h
+++ b/bin/varnishtest/vtc.h
@@ -137,3 +137,8 @@ void b64_settings(const struct http *hp, const char *s);
struct vsb *vtc_hex_to_bin(struct vtclog *vl, const char *arg);
void vtc_expect(struct vtclog *, const char *, const char *, const char *,
const char *, const char *);
+
+/* vtc_term.c */
+struct term *Term_New(struct vtclog *);
+void Term_Feed(struct term *, const char *, const char *);
+void Term_Dump(const struct term *);
diff --git a/bin/varnishtest/vtc_process.c b/bin/varnishtest/vtc_process.c
index dde798b..6d55975 100644
--- a/bin/varnishtest/vtc_process.c
+++ b/bin/varnishtest/vtc_process.c
@@ -81,14 +81,8 @@ struct process {
unsigned hasthread;
int status;
- unsigned term_state;
-#define NTERMARG 10
- int term_arg[NTERMARG];
- int *term_ap;
-#define NLINES 24
-#define NCOLS 80
- char *term_c[NLINES];
- int term_x, term_y;
+ struct term *term;
+
};
static VTAILQ_HEAD(, process) processes =
@@ -113,7 +107,6 @@ process_new(const char *name)
struct process *p;
struct vsb *vsb;
char buf[1024];
- int i;
ALLOC_OBJ(p, PROCESS_MAGIC);
AN(p);
@@ -134,12 +127,8 @@ process_new(const char *name)
p->fd_term = -1;
VTAILQ_INSERT_TAIL(&processes, p, list);
- for (i = 0; i < NLINES; i++) {
- p->term_c[i] = malloc(NCOLS + 1);
- AN(p->term_c[i]);
- memset(p->term_c[i], ' ', NCOLS);
- p->term_c[i][NCOLS] = '\0';
- }
+ p->term = Term_New(p->vl);
+ AN(p->term);
return (p);
}
@@ -182,192 +171,6 @@ process_undef(const struct process *p)
}
/**********************************************************************
- * Terminal emulation
- */
-
-static void
-process_term_clear(const struct process *p)
-{
- int i;
-
- for (i = 0; i < NLINES; i++) {
- memset(p->term_c[i], ' ', NCOLS);
- p->term_c[i][NCOLS] = '\0';
- }
-}
-
-static void
-process_screen_dump(const struct process *p)
-{
- int i;
-
- for (i = 0; i < NLINES; i++)
- vtc_dump(p->vl, 3, "screen", p->term_c[i], NCOLS);
-}
-
-static void
-process_escape(struct process *p, int c, int n)
-{
- int i;
-
- for (i = 0; i < NTERMARG; i++)
- if (!p->term_arg[i])
- p->term_arg[i] = 1;
- switch(c) {
- case 'h':
- if (p->term_arg[0] <= NLINES && p->term_arg[1] <= NCOLS) {
- p->term_y = p->term_arg[0] - 1;
- p->term_x = p->term_arg[1] - 1;
- } else {
- vtc_log(p->vl, 4, "ANSI H %d %d WRONG",
- p->term_arg[0], p->term_arg[1]);
- }
- break;
- case 'j':
- if (p->term_arg[0] == 2) {
- process_term_clear(p);
- } else {
- vtc_log(p->vl, 4, "ANSI J %d", p->term_arg[0]);
- }
- break;
- default:
- for (i = 0; i < n; i++)
- vtc_log(p->vl, 4, "ANSI arg %d",
- p->term_arg[i]);
- vtc_log(p->vl, 4, "ANSI unk '%c'", c);
- break;
- }
-}
-
-static void
-process_scroll(struct process *p)
-{
- int i;
- char *l;
-
- l = p->term_c[0];
- for(i = 0; i < NLINES -1; i++)
- p->term_c[i] = p->term_c[i + 1];
- p->term_c[i] = l;
- memset(l, ' ', NCOLS);
-}
-
-static void
-process_char(struct process *p, char c)
-{
- assert(p->term_x < NCOLS);
- assert(p->term_y < NLINES);
- assert(p->term_state <= 3);
- switch (c) {
- case 0x00:
- break;
- case '\b':
- if (p->term_x > 0)
- p->term_x--;
- break;
- case '\t':
- while(++p->term_x % 8)
- continue;
- if (p->term_x >= NCOLS) {
- p->term_x = 0;
- process_char(p, '\n');
- }
- break;
- case '\n':
- if (p->term_y == NLINES - 1)
- process_scroll(p);
- else
- p->term_y++;
- break;
- case '\r':
- p->term_x = 0;
- break;
- default:
- if (c < ' ' || c > '~')
- vtc_log(p->vl, 4, "ANSI CTRL 0x%02x", c);
- else {
- p->term_c[p->term_y][p->term_x] = c;
- if (p->term_x == NCOLS - 1) {
- p->term_x = 0;
- process_char(p, '\n');
- } else {
- p->term_x++;
- }
- }
- }
-}
-
-static void
-process_ansi(struct process *p, const char *b, const char *e)
-{
-
- while (b < e) {
- assert(p->term_x < NCOLS);
- assert(p->term_y < NLINES);
- assert(p->term_state <= 3);
- switch (p->term_state) {
- case 0:
- if (*b == '\x1b')
- p->term_state = 1;
- else if (*(const uint8_t*)b == 0x9b)
- p->term_state = 2;
- else
- process_char(p, *b);
- b++;
- break;
- case 1:
- if (*b++ == '[') {
- p->term_state = 2;
- } else {
- vtc_log(p->vl, 4, "ANSI not [ 0x%x", b[-1]);
- p->term_state = 0;
- }
- break;
- case 2:
- p->term_ap = p->term_arg;
- memset(p->term_arg, 0, sizeof p->term_arg);
- p->term_state = 3;
- break;
- case 3:
- if (p->term_ap - p->term_arg >= NTERMARG) {
- vtc_log(p->vl, 4, "ANSI too many ;");
- p->term_state = 0;
- b++;
- continue;
- }
- if (isdigit(*b)) {
- *p->term_ap *= 10;
- *p->term_ap += *b++ - '0';
- continue;
- }
- if (*b == ';') {
- p->term_ap++;
- p->term_state = 3;
- b++;
- continue;
- }
- if (islower(*b)) {
- process_escape(p, *b++,
- p->term_ap - p->term_arg);
- p->term_state = 2;
- } else if (isupper(*b)) {
- process_escape(p, tolower(*b++),
- p->term_ap - p->term_arg);
- p->term_ap = p->term_arg;
- p->term_state = 0;
- } else {
- vtc_log(p->vl, 4, "ANSI non-letter %c", *b);
- p->term_state = 0;
- b++;
- }
- break;
- default:
- WRONG("Wrong ansi state");
- }
- }
-}
-
-/**********************************************************************
* Data stream handling
*/
@@ -402,7 +205,7 @@ process_stdout(const struct vev *ev, int what)
else if (p->log == 3)
vtc_hexdump(p->vl, 4, "stdout", buf, i);
(void)write(p->f_stdout, buf, i);
- process_ansi(p, buf, buf + i);
+ Term_Feed(p->term, buf, buf + i);
return (0);
}
@@ -895,7 +698,7 @@ cmd_process(CMD_ARGS)
continue;
}
if (!strcmp(*av, "-screen_dump")) {
- process_screen_dump(p);
+ Term_Dump(p->term);
continue;
}
if (!strcmp(*av, "-close")) {
diff --git a/bin/varnishtest/vtc_term.c b/bin/varnishtest/vtc_term.c
new file mode 100644
index 0000000..34caa30
--- /dev/null
+++ b/bin/varnishtest/vtc_term.c
@@ -0,0 +1,244 @@
+/*-
+ * Copyright (c) 2018 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Poul-Henning Kamp <phk at FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * A trivial ANSI terminal emulation
+ */
+
+#include "config.h"
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "vtc.h"
+
+struct term {
+ unsigned magic;
+#define TERM_MAGIC 0x1c258f0f
+
+ struct vtclog *vl;
+ unsigned state;
+#define NTERMARG 10
+ int arg[NTERMARG];
+ int *argp;
+ int nlin;
+ int ncol;
+ char **vram;
+ int col;
+ int line;
+};
+
+static void
+term_clear(const struct term *tp)
+{
+ int i;
+
+ for (i = 0; i < tp->nlin; i++) {
+ memset(tp->vram[i], ' ', tp->ncol);
+ tp->vram[i][tp->ncol] = '\0';
+ }
+}
+
+void
+Term_Dump(const struct term *tp)
+{
+ int i;
+
+ for (i = 0; i < tp->nlin; i++)
+ vtc_dump(tp->vl, 3, "screen", tp->vram[i], tp->ncol);
+}
+
+static void
+term_escape(struct term *tp, int c, int n)
+{
+ int i;
+
+ c = tolower(c);
+ for (i = 0; i < NTERMARG; i++)
+ if (!tp->arg[i])
+ tp->arg[i] = 1;
+ switch(c) {
+ case 'h':
+ if (tp->arg[0] > tp->nlin || tp->arg[1] > tp->ncol)
+ vtc_fatal(tp->vl, "ANSI H[%d,%d] outside vram",
+ tp->arg[0], tp->arg[1]);
+ tp->line = tp->arg[0] - 1;
+ tp->col = tp->arg[1] - 1;
+ break;
+ case 'j':
+ if (tp->arg[0] != 2)
+ vtc_fatal(tp->vl, "ANSI J[%d]", tp->arg[0]);
+ term_clear(tp);
+ break;
+ default:
+ for (i = 0; i < n; i++)
+ vtc_log(tp->vl, 4, "ANSI arg %d", tp->arg[i]);
+ vtc_fatal(tp->vl, "ANSI unknown (%c)", c);
+ break;
+ }
+}
+
+static void
+term_scroll(const struct term *tp)
+{
+ int i;
+ char *l;
+
+ l = tp->vram[0];
+ for(i = 0; i < tp->nlin -1; i++)
+ tp->vram[i] = tp->vram[i + 1];
+ tp->vram[i] = l;
+ memset(l, ' ', tp->ncol);
+}
+
+static void
+term_char(struct term *tp, char c)
+{
+ assert(tp->col < tp->ncol);
+ assert(tp->line < tp->nlin);
+ assert(tp->state <= 3);
+ switch (c) {
+ case 0x00:
+ break;
+ case '\b':
+ if (tp->col > 0)
+ tp->col--;
+ break;
+ case '\t':
+ while(++tp->col % 8)
+ continue;
+ if (tp->col >= tp->ncol) {
+ tp->col = 0;
+ term_char(tp, '\n');
+ }
+ break;
+ case '\n':
+ if (tp->line == tp->nlin - 1)
+ term_scroll(tp);
+ else
+ tp->line++;
+ break;
+ case '\r':
+ tp->col = 0;
+ break;
+ default:
+ if (c < ' ' || c > '~')
+ c = '?';
+ tp->vram[tp->line][tp->col] = c;
+ if (tp->col == tp->ncol - 1) {
+ tp->col = 0;
+ term_char(tp, '\n');
+ } else {
+ tp->col++;
+ }
+ }
+}
+
+void
+Term_Feed(struct term *tp, const char *b, const char *e)
+{
+
+ while (b < e) {
+ assert(tp->col < tp->ncol);
+ assert(tp->line < tp->nlin);
+ assert(tp->state <= 3);
+ switch (tp->state) {
+ case 0:
+ if (*b == '\x1b')
+ tp->state = 1;
+ else if (*(const uint8_t*)b == 0x9b)
+ tp->state = 2;
+ else
+ term_char(tp, *b);
+ b++;
+ break;
+ case 1:
+ if (*b++ != '[')
+ vtc_fatal(tp->vl, "ANSI not '[' (0x%x)",
+ b[-1] & 0xff);
+ tp->state = 2;
+ break;
+ case 2:
+ tp->argp = tp->arg;
+ memset(tp->arg, 0, sizeof tp->arg);
+ tp->state = 3;
+ break;
+ case 3:
+ if (tp->argp - tp->arg >= NTERMARG)
+ vtc_fatal(tp->vl, "ANSI too many args");
+
+ if (isdigit(*b)) {
+ *tp->argp *= 10;
+ *tp->argp += *b++ - '0';
+ continue;
+ }
+ if (*b == ';') {
+ tp->argp++;
+ tp->state = 3;
+ b++;
+ continue;
+ }
+ if (islower(*b)) {
+ term_escape(tp, *b++, tp->argp - tp->arg);
+ tp->state = 2;
+ } else if (isupper(*b)) {
+ term_escape(tp, *b++, tp->argp - tp->arg);
+ tp->argp = tp->arg;
+ tp->state = 0;
+ } else {
+ vtc_fatal(tp->vl, "ANSI non-letter (0x%02x)",
+ *b & 0xff);
+ }
+ break;
+ default:
+ WRONG("Wrong ansi state");
+ }
+ }
+}
+
+struct term *
+Term_New(struct vtclog *vl)
+{
+ struct term *tp;
+ int i;
+
+ ALLOC_OBJ(tp, TERM_MAGIC);
+ AN(tp);
+ tp->vl = vl;
+ tp->nlin = 24;
+ tp->ncol = 80;
+ tp->vram = calloc(tp->nlin, sizeof *tp->vram);
+ AN(tp->vram);
+ for (i = 0; i < tp->nlin; i++) {
+ tp->vram[i] = malloc(tp->ncol + 1L);
+ AN(tp->vram[i]);
+ }
+ term_clear(tp);
+ tp->line = tp->nlin - 1;
+ return (tp);
+}
+
More information about the varnish-commit
mailing list