summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorMiod Vallat <miod@cvs.openbsd.org>2009-09-05 14:49:21 +0000
committerMiod Vallat <miod@cvs.openbsd.org>2009-09-05 14:49:21 +0000
commitec17529a5bd62e044b53345c38f067d1451d619b (patch)
tree4db8b16a998a02869773fbb021ddbd2d11ca0d63 /sys
parentc818bfbdec899fc627d95f7d7149b9425f7f9188 (diff)
Check the return value of all emulops in the emulation code, and abort
tty output as soon as we hit a failure. Since the `output' of a character may cause several emulops to be called (e.g. if it causes scrollup or if this is the end of an escape sequence), all emulation code maintain a so-called `abort state', to be able to properly recover when the character is tentatively output later, and not reissue the emulops which did not fail the first time. With help from mglocker@
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/wscons/wsemul_dumb.c108
-rw-r--r--sys/dev/wscons/wsemul_sun.c292
-rw-r--r--sys/dev/wscons/wsemul_vt100.c284
-rw-r--r--sys/dev/wscons/wsemul_vt100_keys.c4
-rw-r--r--sys/dev/wscons/wsemul_vt100_subr.c189
-rw-r--r--sys/dev/wscons/wsemul_vt100var.h21
-rw-r--r--sys/dev/wscons/wsemulvar.h114
7 files changed, 777 insertions, 235 deletions
diff --git a/sys/dev/wscons/wsemul_dumb.c b/sys/dev/wscons/wsemul_dumb.c
index 5dedc3e24ea..08358d122a9 100644
--- a/sys/dev/wscons/wsemul_dumb.c
+++ b/sys/dev/wscons/wsemul_dumb.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: wsemul_dumb.c,v 1.8 2009/09/05 14:30:24 miod Exp $ */
+/* $OpenBSD: wsemul_dumb.c,v 1.9 2009/09/05 14:49:20 miod Exp $ */
/* $NetBSD: wsemul_dumb.c,v 1.7 2000/01/05 11:19:36 drochner Exp $ */
/*
@@ -44,7 +44,7 @@
void *wsemul_dumb_cnattach(const struct wsscreen_descr *, void *,
int, int, long);
-void *wsemul_dumb_attach(int console, const struct wsscreen_descr *,
+void *wsemul_dumb_attach(int, const struct wsscreen_descr *,
void *, int, int, void *, long);
u_int wsemul_dumb_output(void *, const u_char *, u_int, int);
int wsemul_dumb_translate(void *, keysym_t, const char **);
@@ -63,6 +63,7 @@ const struct wsemul_ops wsemul_dumb_ops = {
struct wsemul_dumb_emuldata {
const struct wsdisplay_emulops *emulops;
+ struct wsemul_abortstate abortstate;
void *emulcookie;
void *cbcookie;
int crippled;
@@ -95,6 +96,7 @@ wsemul_dumb_cnattach(type, cookie, ccol, crow, defattr)
edp->crippled = emulops->cursor == NULL ||
emulops->copycols == NULL || emulops->copyrows == NULL ||
emulops->erasecols == NULL || emulops->eraserows == NULL;
+ wsemul_reset_abortstate(&edp->abortstate);
return (edp);
}
@@ -122,6 +124,7 @@ wsemul_dumb_attach(console, type, cookie, ccol, crow, cbcookie, defattr)
edp->crow = crow;
edp->ccol = ccol;
edp->defattr = defattr;
+ wsemul_reset_abortstate(&edp->abortstate);
}
edp->cbcookie = cbcookie;
@@ -140,26 +143,54 @@ wsemul_dumb_output(cookie, data, count, kernel)
u_int processed = 0;
u_char c;
int n;
+ int rc = 0;
if (edp->crippled) {
while (count-- > 0) {
- c = *data++;
+ wsemul_resume_abort(&edp->abortstate);
+ c = *data++;
if (c == ASCII_BEL)
wsdisplay_emulbell(edp->cbcookie);
- else
- (*edp->emulops->putchar)(edp->emulcookie, 0,
- 0, c, 0);
+ else {
+ WSEMULOP(rc, edp, &edp->abortstate, putchar,
+ (edp->emulcookie, 0, 0, c, 0));
+ if (rc != 0)
+ break;
+ }
processed++;
}
+ if (rc != 0)
+ wsemul_abort_other(&edp->abortstate);
return processed;
}
- /* XXX */
- (*edp->emulops->cursor)(edp->emulcookie, 0, edp->crow, edp->ccol);
+ switch (edp->abortstate.state) {
+ case ABORT_FAILED_CURSOR:
+ /*
+ * If we could not display the cursor back, we pretended not
+ * having been able to display the last character. But this
+ * is a lie, so compensate here.
+ */
+ data++, count--;
+ processed++;
+ wsemul_reset_abortstate(&edp->abortstate);
+ break;
+ case ABORT_OK:
+ /* remove cursor image */
+ rc = (*edp->emulops->cursor)
+ (edp->emulcookie, 0, edp->crow, edp->ccol);
+ if (rc != 0)
+ return 0;
+ break;
+ default:
+ break;
+ }
+
while (count-- > 0) {
- c = *data++;
+ wsemul_resume_abort(&edp->abortstate);
+ c = *data++;
switch (c) {
case ASCII_BEL:
wsdisplay_emulbell(edp->cbcookie);
@@ -177,14 +208,19 @@ wsemul_dumb_output(cookie, data, count, kernel)
case ASCII_HT:
n = min(8 - (edp->ccol & 7),
edp->ncols - edp->ccol - 1);
- (*edp->emulops->erasecols)(edp->emulcookie,
- edp->crow, edp->ccol, n, edp->defattr);
+ WSEMULOP(rc, edp, &edp->abortstate, erasecols,
+ (edp->emulcookie, edp->crow, edp->ccol, n,
+ edp->defattr));
+ if (rc != 0)
+ break;
edp->ccol += n;
break;
case ASCII_FF:
- (*edp->emulops->eraserows)(edp->emulcookie, 0,
- edp->nrows, edp->defattr);
+ WSEMULOP(rc, edp, &edp->abortstate, eraserows,
+ (edp->emulcookie, 0, edp->nrows, edp->defattr));
+ if (rc != 0)
+ break;
edp->ccol = 0;
edp->crow = 0;
break;
@@ -195,8 +231,11 @@ wsemul_dumb_output(cookie, data, count, kernel)
break;
default:
- (*edp->emulops->putchar)(edp->emulcookie, edp->crow,
- edp->ccol, c, edp->defattr);
+ WSEMULOP(rc, edp, &edp->abortstate, putchar,
+ (edp->emulcookie, edp->crow, edp->ccol, c,
+ edp->defattr));
+ if (rc != 0)
+ break;
edp->ccol++;
/* if cur col is still on cur line, done. */
@@ -215,17 +254,44 @@ wsemul_dumb_output(cookie, data, count, kernel)
break;
}
n = 1; /* number of lines to scroll */
- (*edp->emulops->copyrows)(edp->emulcookie, n, 0,
- edp->nrows - n);
- (*edp->emulops->eraserows)(edp->emulcookie,
- edp->nrows - n, n, edp->defattr);
+ WSEMULOP(rc, edp, &edp->abortstate, copyrows,
+ (edp->emulcookie, n, 0, edp->nrows - n));
+ if (rc == 0)
+ WSEMULOP(rc, edp, &edp->abortstate, eraserows,
+ (edp->emulcookie, edp->nrows - n, n,
+ edp->defattr));
+ if (rc != 0) {
+ /* undo wrap-at-eol processing if necessary */
+ if (c != ASCII_LF)
+ edp->ccol = edp->ncols - 1;
+ break;
+ }
edp->crow -= n - 1;
break;
}
+ if (rc != 0)
+ break;
processed++;
}
- /* XXX */
- (*edp->emulops->cursor)(edp->emulcookie, 1, edp->crow, edp->ccol);
+
+ if (rc != 0)
+ wsemul_abort_other(&edp->abortstate);
+ else {
+ /* put cursor image back */
+ rc = (*edp->emulops->cursor)
+ (edp->emulcookie, 1, edp->crow, edp->ccol);
+ if (rc != 0) {
+ /*
+ * Fail the last character output, remembering that
+ * only the cursor operation really needs to be done.
+ */
+ wsemul_abort_cursor(&edp->abortstate);
+ processed--;
+ }
+ }
+
+ if (rc == 0)
+ wsemul_reset_abortstate(&edp->abortstate);
return processed;
}
diff --git a/sys/dev/wscons/wsemul_sun.c b/sys/dev/wscons/wsemul_sun.c
index 8ca6c9cd322..3edf4b19801 100644
--- a/sys/dev/wscons/wsemul_sun.c
+++ b/sys/dev/wscons/wsemul_sun.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: wsemul_sun.c,v 1.26 2009/09/05 14:30:24 miod Exp $ */
+/* $OpenBSD: wsemul_sun.c,v 1.27 2009/09/05 14:49:20 miod Exp $ */
/* $NetBSD: wsemul_sun.c,v 1.11 2000/01/05 11:19:36 drochner Exp $ */
/*
@@ -81,6 +81,7 @@ const struct wsemul_ops wsemul_sun_ops = {
struct wsemul_sun_emuldata {
const struct wsdisplay_emulops *emulops;
+ struct wsemul_abortstate abortstate;
void *emulcookie;
void *cbcookie;
int scrcapabilities;
@@ -106,14 +107,14 @@ void wsemul_sun_init(struct wsemul_sun_emuldata *,
int wsemul_sun_jump_scroll(struct wsemul_sun_emuldata *, const u_char *,
u_int, int);
void wsemul_sun_reset(struct wsemul_sun_emuldata *);
-void wsemul_sun_output_lowchars(struct wsemul_sun_emuldata *, u_char, int);
-void wsemul_sun_output_normal(struct wsemul_sun_emuldata *, u_char, int);
+int wsemul_sun_output_lowchars(struct wsemul_sun_emuldata *, u_char, int);
+int wsemul_sun_output_normal(struct wsemul_sun_emuldata *, u_char, int);
void wsemul_sun_output_haveesc(struct wsemul_sun_emuldata *, u_char);
-void wsemul_sun_output_control(struct wsemul_sun_emuldata *, u_char);
-void wsemul_sun_control(struct wsemul_sun_emuldata *, u_char);
+int wsemul_sun_output_control(struct wsemul_sun_emuldata *, u_char);
+int wsemul_sun_control(struct wsemul_sun_emuldata *, u_char);
int wsemul_sun_selectattribute(struct wsemul_sun_emuldata *, int, int, int,
long *, long *);
-void wsemul_sun_scrollup(struct wsemul_sun_emuldata *, u_int);
+int wsemul_sun_scrollup(struct wsemul_sun_emuldata *, u_int);
struct wsemul_sun_emuldata wsemul_sun_console_emuldata;
@@ -137,6 +138,7 @@ wsemul_sun_init(struct wsemul_sun_emuldata *edp,
edp->crow = crow;
edp->ccol = ccol;
edp->defattr = defattr;
+ wsemul_reset_abortstate(&edp->abortstate);
}
void
@@ -222,11 +224,12 @@ wsemul_sun_attach(int console, const struct wsscreen_descr *type, void *cookie,
return (edp);
}
-void
+int
wsemul_sun_output_lowchars(struct wsemul_sun_emuldata *edp, u_char c,
int kernel)
{
u_int n;
+ int rc = 0;
switch (c) {
case ASCII_NUL:
@@ -250,16 +253,20 @@ wsemul_sun_output_lowchars(struct wsemul_sun_emuldata *edp, u_char c,
case ASCII_HT: /* "Tab (TAB)" */
n = min(8 - (edp->ccol & 7), COLS_LEFT);
if (n != 0) {
- (*edp->emulops->erasecols)(edp->emulcookie, edp->crow,
- edp->ccol, n,
- kernel ? edp->kernattr : edp->bkgdattr);
+ WSEMULOP(rc, edp, &edp->abortstate, erasecols,
+ (edp->emulcookie, edp->crow, edp->ccol, n,
+ kernel ? edp->kernattr : edp->bkgdattr));
+ if (rc != 0)
+ break;
edp->ccol += n;
}
break;
case ASCII_FF: /* "Form Feed (FF)" */
- (*edp->emulops->eraserows)(edp->emulcookie, 0, edp->nrows,
- edp->bkgdattr);
+ WSEMULOP(rc, edp, &edp->abortstate, eraserows,
+ (edp->emulcookie, 0, edp->nrows, edp->bkgdattr));
+ if (rc != 0)
+ break;
edp->ccol = edp->crow = 0;
break;
@@ -282,27 +289,45 @@ wsemul_sun_output_lowchars(struct wsemul_sun_emuldata *edp, u_char c,
/* if the cur line isn't the last, incr and leave. */
if (ROWS_LEFT > 0)
edp->crow++;
- else
- wsemul_sun_scrollup(edp, edp->scrolldist);
+ else {
+ rc = wsemul_sun_scrollup(edp, edp->scrolldist);
+ if (rc != 0)
+ break;
+ }
break;
}
+
+ return rc;
}
-void
+int
wsemul_sun_output_normal(struct wsemul_sun_emuldata *edp, u_char c, int kernel)
{
+ int rc;
- (*edp->emulops->putchar)(edp->emulcookie, edp->crow, edp->ccol,
- c, kernel ? edp->kernattr : edp->curattr);
+ WSEMULOP(rc, edp, &edp->abortstate, putchar,
+ (edp->emulcookie, edp->crow, edp->ccol,
+ c, kernel ? edp->kernattr : edp->curattr));
+ if (rc != 0)
+ return rc;
if (++edp->ccol >= edp->ncols) {
/* if the cur line isn't the last, incr and leave. */
if (ROWS_LEFT > 0)
edp->crow++;
- else
- wsemul_sun_scrollup(edp, edp->scrolldist);
+ else {
+ rc = wsemul_sun_scrollup(edp, edp->scrolldist);
+ if (rc != 0) {
+ /* undo line wrap */
+ edp->ccol--;
+
+ return rc;
+ }
+ }
edp->ccol = 0;
}
+
+ return 0;
}
void
@@ -324,12 +349,13 @@ wsemul_sun_output_haveesc(struct wsemul_sun_emuldata *edp, u_char c)
}
}
-void
+int
wsemul_sun_control(struct wsemul_sun_emuldata *edp, u_char c)
{
u_int n, src, dst;
int flags, fgcol, bgcol;
long attr, bkgdattr;
+ int rc = 0;
switch (c) {
case '@': /* "Insert Character (ICH)" */
@@ -337,11 +363,14 @@ wsemul_sun_control(struct wsemul_sun_emuldata *edp, u_char c)
src = edp->ccol;
dst = edp->ccol + n;
if (dst < edp->ncols) {
- (*edp->emulops->copycols)(edp->emulcookie, edp->crow,
- src, dst, edp->ncols - dst);
+ WSEMULOP(rc, edp, &edp->abortstate, copycols,
+ (edp->emulcookie, edp->crow, src, dst,
+ edp->ncols - dst));
+ if (rc != 0)
+ break;
}
- (*edp->emulops->erasecols)(edp->emulcookie, edp->crow,
- src, n, edp->bkgdattr);
+ WSEMULOP(rc, edp, &edp->abortstate, erasecols,
+ (edp->emulcookie, edp->crow, src, n, edp->bkgdattr));
break;
case 'A': /* "Cursor Up (CUU)" */
@@ -371,13 +400,17 @@ wsemul_sun_control(struct wsemul_sun_emuldata *edp, u_char c)
case 'J': /* "Erase in Display (ED)" */
if (ROWS_LEFT > 0) {
- (*edp->emulops->eraserows)(edp->emulcookie,
- edp->crow + 1, ROWS_LEFT, edp->bkgdattr);
+ WSEMULOP(rc, edp, &edp->abortstate, eraserows,
+ (edp->emulcookie, edp->crow + 1, ROWS_LEFT,
+ edp->bkgdattr));
+ if (rc != 0)
+ break;
}
/* FALLTHROUGH */
case 'K': /* "Erase in Line (EL)" */
- (*edp->emulops->erasecols)(edp->emulcookie, edp->crow,
- edp->ccol, COLS_LEFT + 1, edp->bkgdattr);
+ WSEMULOP(rc, edp, &edp->abortstate, erasecols,
+ (edp->emulcookie, edp->crow, edp->ccol, COLS_LEFT + 1,
+ edp->bkgdattr));
break;
case 'L': /* "Insert Line (IL)" */
@@ -385,11 +418,13 @@ wsemul_sun_control(struct wsemul_sun_emuldata *edp, u_char c)
src = edp->crow;
dst = edp->crow + n;
if (dst < edp->nrows) {
- (*edp->emulops->copyrows)(edp->emulcookie,
- src, dst, edp->nrows - dst);
+ WSEMULOP(rc, edp, &edp->abortstate, copyrows,
+ (edp->emulcookie, src, dst, edp->nrows - dst));
+ if (rc != 0)
+ break;
}
- (*edp->emulops->eraserows)(edp->emulcookie,
- src, n, edp->bkgdattr);
+ WSEMULOP(rc, edp, &edp->abortstate, eraserows,
+ (edp->emulcookie, src, n, edp->bkgdattr));
break;
case 'M': /* "Delete Line (DL)" */
@@ -397,11 +432,14 @@ wsemul_sun_control(struct wsemul_sun_emuldata *edp, u_char c)
src = edp->crow + n;
dst = edp->crow;
if (src < edp->nrows) {
- (*edp->emulops->copyrows)(edp->emulcookie,
- src, dst, edp->nrows - src);
+ WSEMULOP(rc, edp, &edp->abortstate, copyrows,
+ (edp->emulcookie, src, dst, edp->nrows - src));
+ if (rc != 0)
+ break;
}
- (*edp->emulops->eraserows)(edp->emulcookie,
- dst + edp->nrows - src, n, edp->bkgdattr);
+ WSEMULOP(rc, edp, &edp->abortstate, eraserows,
+ (edp->emulcookie, dst + edp->nrows - src, n,
+ edp->bkgdattr));
break;
case 'P': /* "Delete Character (DCH)" */
@@ -409,11 +447,15 @@ wsemul_sun_control(struct wsemul_sun_emuldata *edp, u_char c)
src = edp->ccol + n;
dst = edp->ccol;
if (src < edp->ncols) {
- (*edp->emulops->copycols)(edp->emulcookie, edp->crow,
- src, dst, edp->ncols - src);
+ WSEMULOP(rc, edp, &edp->abortstate, copycols,
+ (edp->emulcookie, edp->crow, src, dst,
+ edp->ncols - src));
+ if (rc != 0)
+ break;
}
- (*edp->emulops->erasecols)(edp->emulcookie, edp->crow,
- edp->ncols - n, n, edp->bkgdattr);
+ WSEMULOP(rc, edp, &edp->abortstate, erasecols,
+ (edp->emulcookie, edp->crow, edp->ncols - n, n,
+ edp->bkgdattr));
break;
case 'm': /* "Select Graphic Rendition (SGR)" */
@@ -431,7 +473,7 @@ wsemul_sun_control(struct wsemul_sun_emuldata *edp, u_char c)
edp->attrflags = 0;
edp->fgcol = WSCOL_BLACK;
edp->bgcol = WSCOL_WHITE;
- return;
+ return 0;
}
flags = 0;
fgcol = WSCOL_BLACK;
@@ -497,11 +539,16 @@ setattr:
wsemul_sun_reset(edp);
break;
}
+
+ return rc;
}
-void
+int
wsemul_sun_output_control(struct wsemul_sun_emuldata *edp, u_char c)
{
+ int oargs;
+ int rc;
+
switch (c) {
case '0': case '1': case '2': case '3': case '4': /* argument digit */
case '5': case '6': case '7': case '8': case '9':
@@ -523,13 +570,21 @@ wsemul_sun_output_control(struct wsemul_sun_emuldata *edp, u_char c)
break;
default: /* end of escape sequence */
- edp->nargs++;
+ oargs = edp->nargs++;
if (edp->nargs > SUN_EMUL_NARGS)
edp->nargs = SUN_EMUL_NARGS;
- wsemul_sun_control(edp, c);
+ rc = wsemul_sun_control(edp, c);
+ if (rc != 0) {
+ /* undo nargs progress */
+ edp->nargs = oargs;
+
+ return rc;
+ }
edp->state = SUN_EMUL_STATE_NORMAL;
break;
}
+
+ return 0;
}
u_int
@@ -541,57 +596,112 @@ wsemul_sun_output(void *cookie, const u_char *data, u_int count, int kernel)
#ifdef JUMP_SCROLL
int lines;
#endif
+ int rc = 0;
#ifdef DIAGNOSTIC
if (kernel && !edp->console)
panic("wsemul_sun_output: kernel output, not console");
#endif
- /* XXX */
- (*edp->emulops->cursor)(edp->emulcookie, 0, edp->crow, edp->ccol);
+ switch (edp->abortstate.state) {
+ case ABORT_FAILED_CURSOR:
+ /*
+ * If we could not display the cursor back, we pretended not
+ * having been able to display the last character. But this
+ * is a lie, so compensate here.
+ */
+ data++, count--;
+ processed++;
+ wsemul_reset_abortstate(&edp->abortstate);
+ break;
+ case ABORT_OK:
+ /* remove cursor image */
+ rc = (*edp->emulops->cursor)
+ (edp->emulcookie, 0, edp->crow, edp->ccol);
+ if (rc != 0)
+ return 0;
+ break;
+ default:
+ break;
+ }
for (; count > 0; data++, count--) {
+ wsemul_resume_abort(&edp->abortstate);
+
#ifdef JUMP_SCROLL
- /*
- * If scrolling is not disabled and we are the bottom of
- * the screen, count newlines until an escape sequence
- * appears.
- */
- if ((edp->state == SUN_EMUL_STATE_NORMAL || kernel) &&
- ROWS_LEFT == 0 && edp->scrolldist != 0)
- lines = wsemul_sun_jump_scroll(edp, data,
- count, kernel);
- else
+ switch (edp->abortstate.state) {
+ case ABORT_FAILED_JUMP_SCROLL:
+ /*
+ * If we failed a previous jump scroll attempt, we
+ * need to try to resume it with the same distance.
+ * We can not recompute it since there might be more
+ * bytes in the tty ring, causing a different result.
+ */
+ lines = edp->abortstate.lines;
+ break;
+ case ABORT_OK:
+ /*
+ * If scrolling is not disabled and we are the bottom of
+ * the screen, count newlines until an escape sequence
+ * appears.
+ */
+ if ((edp->state == SUN_EMUL_STATE_NORMAL || kernel) &&
+ ROWS_LEFT == 0 && edp->scrolldist != 0)
+ lines = wsemul_sun_jump_scroll(edp, data,
+ count, kernel);
+ else
+ lines = 0;
+ break;
+ default:
+ /*
+ * If we are recovering a non-scrolling failure,
+ * do not try to scroll yet.
+ */
lines = 0;
+ break;
+ }
if (lines > 1) {
- wsemul_sun_scrollup(edp, lines);
+ wsemul_resume_abort(&edp->abortstate);
+ rc = wsemul_sun_scrollup(edp, lines);
+ if (rc != 0) {
+ wsemul_abort_jump_scroll(&edp->abortstate,
+ lines);
+ return processed;
+ }
+ wsemul_reset_abortstate(&edp->abortstate);
edp->crow--;
}
#endif
+ wsemul_resume_abort(&edp->abortstate);
+
c = *data;
if (c < ' ') {
- wsemul_sun_output_lowchars(edp, c, kernel);
+ rc = wsemul_sun_output_lowchars(edp, c, kernel);
+ if (rc != 0)
+ break;
processed++;
continue;
}
if (kernel) {
- wsemul_sun_output_normal(edp, c, 1);
+ rc = wsemul_sun_output_normal(edp, c, 1);
+ if (rc != 0)
+ break;
processed++;
continue;
}
switch (edp->state) {
case SUN_EMUL_STATE_NORMAL:
- wsemul_sun_output_normal(edp, c, 0);
+ rc = wsemul_sun_output_normal(edp, c, 0);
break;
case SUN_EMUL_STATE_HAVEESC:
wsemul_sun_output_haveesc(edp, c);
break;
case SUN_EMUL_STATE_CONTROL:
- wsemul_sun_output_control(edp, c);
+ rc = wsemul_sun_output_control(edp, c);
break;
default:
#ifdef DIAGNOSTIC
@@ -599,14 +709,33 @@ wsemul_sun_output(void *cookie, const u_char *data, u_int count, int kernel)
#else
/* try to recover, if things get screwed up... */
edp->state = SUN_EMUL_STATE_NORMAL;
- wsemul_sun_output_normal(edp, c, 0);
+ rc = wsemul_sun_output_normal(edp, c, 0);
#endif
break;
}
+ if (rc != 0)
+ break;
processed++;
}
- /* XXX */
- (*edp->emulops->cursor)(edp->emulcookie, 1, edp->crow, edp->ccol);
+
+ if (rc != 0)
+ wsemul_abort_other(&edp->abortstate);
+ else {
+ /* put cursor image back */
+ rc = (*edp->emulops->cursor)
+ (edp->emulcookie, 1, edp->crow, edp->ccol);
+ if (rc != 0) {
+ /*
+ * Fail the last character output, remembering that
+ * only the cursor operation really needs to be done.
+ */
+ wsemul_abort_cursor(&edp->abortstate);
+ processed--;
+ }
+ }
+
+ if (rc == 0)
+ wsemul_reset_abortstate(&edp->abortstate);
return processed;
}
@@ -855,26 +984,31 @@ wsemul_sun_resetop(void *cookie, enum wsemul_resetops op)
(*edp->emulops->cursor)(edp->emulcookie, 1, 0, 0);
break;
case WSEMUL_CLEARCURSOR:
- (*edp->emulops->cursor)(edp->emulcookie, 0,
- edp->crow, edp->ccol);
+ (*edp->emulops->cursor)(edp->emulcookie, 0, edp->crow,
+ edp->ccol);
break;
default:
break;
}
}
-void
+int
wsemul_sun_scrollup(struct wsemul_sun_emuldata *edp, u_int lines)
{
+ int rc;
+
/*
* if we're in wrap-around mode, go to the first
* line and clear it.
*/
if (lines == 0) {
+ WSEMULOP(rc, edp, &edp->abortstate, eraserows,
+ (edp->emulcookie, 0, 1, edp->bkgdattr));
+ if (rc != 0)
+ return rc;
+
edp->crow = 0;
- (*edp->emulops->eraserows)(edp->emulcookie, 0, 1,
- edp->bkgdattr);
- return;
+ return 0;
}
/*
@@ -882,10 +1016,18 @@ wsemul_sun_scrollup(struct wsemul_sun_emuldata *edp, u_int lines)
* (usually 34), clear the screen; otherwise, scroll by the
* scrolling distance.
*/
- if (lines < edp->nrows)
- (*edp->emulops->copyrows)(edp->emulcookie, lines, 0,
- edp->nrows - lines);
- (*edp->emulops->eraserows)(edp->emulcookie,
- edp->nrows - lines, lines, edp->bkgdattr);
+ if (lines < edp->nrows) {
+ WSEMULOP(rc, edp, &edp->abortstate, copyrows,
+ (edp->emulcookie, lines, 0, edp->nrows - lines));
+ if (rc != 0)
+ return rc;
+ }
+ WSEMULOP(rc, edp, &edp->abortstate, eraserows,
+ (edp->emulcookie, edp->nrows - lines, lines, edp->bkgdattr));
+ if (rc != 0)
+ return rc;
+
edp->crow -= lines - 1;
+
+ return 0;
}
diff --git a/sys/dev/wscons/wsemul_vt100.c b/sys/dev/wscons/wsemul_vt100.c
index 3dd48cc8bbd..210d9e53edb 100644
--- a/sys/dev/wscons/wsemul_vt100.c
+++ b/sys/dev/wscons/wsemul_vt100.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: wsemul_vt100.c,v 1.25 2009/09/05 14:30:24 miod Exp $ */
+/* $OpenBSD: wsemul_vt100.c,v 1.26 2009/09/05 14:49:20 miod Exp $ */
/* $NetBSD: wsemul_vt100.c,v 1.13 2000/04/28 21:56:16 mycroft Exp $ */
/*
@@ -67,10 +67,10 @@ void wsemul_vt100_init(struct wsemul_vt100_emuldata *,
const struct wsscreen_descr *, void *, int, int, long);
int wsemul_vt100_jump_scroll(struct wsemul_vt100_emuldata *,
const u_char *, u_int, int);
-void wsemul_vt100_output_normal(struct wsemul_vt100_emuldata *, u_char, int);
-void wsemul_vt100_output_c0c1(struct wsemul_vt100_emuldata *, u_char, int);
-void wsemul_vt100_nextline(struct wsemul_vt100_emuldata *);
-typedef void vt100_handler(struct wsemul_vt100_emuldata *, u_char);
+int wsemul_vt100_output_normal(struct wsemul_vt100_emuldata *, u_char, int);
+int wsemul_vt100_output_c0c1(struct wsemul_vt100_emuldata *, u_char, int);
+int wsemul_vt100_nextline(struct wsemul_vt100_emuldata *);
+typedef int vt100_handler(struct wsemul_vt100_emuldata *, u_char);
vt100_handler
wsemul_vt100_output_esc,
wsemul_vt100_output_csi,
@@ -127,6 +127,7 @@ wsemul_vt100_init(struct wsemul_vt100_emuldata *edp,
edp->crow = crow;
edp->ccol = ccol;
edp->defattr = defattr;
+ wsemul_reset_abortstate(&edp->abortstate);
}
void *
@@ -243,14 +244,14 @@ wsemul_vt100_resetop(void *cookie, enum wsemul_resetops op)
vt100_initchartables(edp);
break;
case WSEMUL_CLEARSCREEN:
- wsemul_vt100_ed(edp, 2);
+ (void)wsemul_vt100_ed(edp, 2);
edp->ccol = edp->crow = 0;
(*edp->emulops->cursor)(edp->emulcookie,
edp->flags & VTFL_CURSORON, 0, 0);
break;
case WSEMUL_CLEARCURSOR:
- (*edp->emulops->cursor)(edp->emulcookie, 0,
- edp->crow, edp->ccol);
+ (*edp->emulops->cursor)(edp->emulcookie, 0, edp->crow,
+ edp->ccol);
break;
default:
break;
@@ -291,33 +292,42 @@ wsemul_vt100_reset(struct wsemul_vt100_emuldata *edp)
* the bottom of the scroll area, then scroll it up. If the cursor is
* at the bottom of the screen then don't move it down.
*/
-void
+int
wsemul_vt100_nextline(struct wsemul_vt100_emuldata *edp)
{
+ int rc;
+
if (ROWS_BELOW == 0) {
/* Bottom of the scroll region. */
- wsemul_vt100_scrollup(edp, 1);
+ rc = wsemul_vt100_scrollup(edp, 1);
} else {
if ((edp->crow+1) < edp->nrows)
/* Cursor not at the bottom of the screen. */
edp->crow++;
CHECK_DW;
+ rc = 0;
}
+
+ return rc;
}
/*
* now all the state machine bits
*/
-void
+int
wsemul_vt100_output_normal(struct wsemul_vt100_emuldata *edp, u_char c,
int kernel)
{
u_int *ct, dc;
+ int oldsschartab = edp->sschartab;
+ int rc = 0;
if ((edp->flags & (VTFL_LASTCHAR | VTFL_DECAWM)) ==
(VTFL_LASTCHAR | VTFL_DECAWM)) {
- wsemul_vt100_nextline(edp);
+ rc = wsemul_vt100_nextline(edp);
+ if (rc != 0)
+ return rc;
edp->ccol = 0;
edp->flags &= ~VTFL_LASTCHAR;
}
@@ -334,23 +344,41 @@ wsemul_vt100_output_normal(struct wsemul_vt100_emuldata *edp, u_char c,
}
dc = (ct ? ct[c] : c);
- if ((edp->flags & VTFL_INSERTMODE) && COLS_LEFT)
- COPYCOLS(edp->ccol, edp->ccol + 1, COLS_LEFT);
+ if ((edp->flags & VTFL_INSERTMODE) && COLS_LEFT) {
+ WSEMULOP(rc, edp, &edp->abortstate, copycols,
+ COPYCOLS(edp->ccol, edp->ccol + 1, COLS_LEFT));
+ if (rc != 0) {
+ /* undo potential sschartab update */
+ edp->sschartab = oldsschartab;
+
+ return rc;
+ }
+ }
+
+ WSEMULOP(rc, edp, &edp->abortstate, putchar,
+ (edp->emulcookie, edp->crow, edp->ccol << edp->dw, dc,
+ kernel ? edp->kernattr : edp->curattr));
+ if (rc != 0) {
+ /* undo potential sschartab update */
+ edp->sschartab = oldsschartab;
- (*edp->emulops->putchar)(edp->emulcookie, edp->crow,
- edp->ccol << edp->dw, dc, kernel ? edp->kernattr : edp->curattr);
+ return rc;
+ }
if (COLS_LEFT)
edp->ccol++;
else
edp->flags |= VTFL_LASTCHAR;
+
+ return 0;
}
-void
+int
wsemul_vt100_output_c0c1(struct wsemul_vt100_emuldata *edp, u_char c,
int kernel)
{
u_int n;
+ int rc = 0;
switch (c) {
case ASCII_NUL:
@@ -430,15 +458,18 @@ wsemul_vt100_output_c0c1(struct wsemul_vt100_emuldata *edp, u_char c,
case ASCII_LF:
case ASCII_VT:
case ASCII_FF:
- wsemul_vt100_nextline(edp);
+ rc = wsemul_vt100_nextline(edp);
break;
}
+
+ return rc;
}
-void
+int
wsemul_vt100_output_esc(struct wsemul_vt100_emuldata *edp, u_char c)
{
u_int newstate = VT100_EMUL_STATE_NORMAL;
+ int rc = 0;
int i;
switch (c) {
@@ -487,7 +518,7 @@ wsemul_vt100_output_esc(struct wsemul_vt100_emuldata *edp, u_char c)
edp->ccol = 0;
/* FALLTHROUGH */
case 'D': /* IND */
- wsemul_vt100_nextline(edp);
+ rc = wsemul_vt100_nextline(edp);
break;
case 'H': /* HTS */
if (edp->tabs != NULL)
@@ -520,7 +551,7 @@ wsemul_vt100_output_esc(struct wsemul_vt100_emuldata *edp, u_char c)
CHECK_DW;
break;
}
- wsemul_vt100_scrolldown(edp, 1);
+ rc = wsemul_vt100_scrolldown(edp, 1);
break;
case 'P': /* DCS */
edp->nargs = 0;
@@ -529,7 +560,9 @@ wsemul_vt100_output_esc(struct wsemul_vt100_emuldata *edp, u_char c)
break;
case 'c': /* RIS */
wsemul_vt100_reset(edp);
- wsemul_vt100_ed(edp, 2);
+ rc = wsemul_vt100_ed(edp, 2);
+ if (rc != 0)
+ break;
edp->ccol = edp->crow = 0;
break;
case '(': case ')': case '*': case '+': /* SCS */
@@ -561,10 +594,14 @@ wsemul_vt100_output_esc(struct wsemul_vt100_emuldata *edp, u_char c)
break;
}
+ if (rc != 0)
+ return rc;
+
edp->state = newstate;
+ return 0;
}
-void
+int
wsemul_vt100_output_scs94(struct wsemul_vt100_emuldata *edp, u_char c)
{
u_int newstate = VT100_EMUL_STATE_NORMAL;
@@ -597,9 +634,10 @@ wsemul_vt100_output_scs94(struct wsemul_vt100_emuldata *edp, u_char c)
}
edp->state = newstate;
+ return 0;
}
-void
+int
wsemul_vt100_output_scs94_percent(struct wsemul_vt100_emuldata *edp, u_char c)
{
switch (c) {
@@ -615,9 +653,10 @@ wsemul_vt100_output_scs94_percent(struct wsemul_vt100_emuldata *edp, u_char c)
}
edp->state = VT100_EMUL_STATE_NORMAL;
+ return 0;
}
-void
+int
wsemul_vt100_output_scs96(struct wsemul_vt100_emuldata *edp, u_char c)
{
u_int newstate = VT100_EMUL_STATE_NORMAL;
@@ -671,9 +710,10 @@ setnrc:
}
edp->state = newstate;
+ return 0;
}
-void
+int
wsemul_vt100_output_scs96_percent(struct wsemul_vt100_emuldata *edp, u_char c)
{
switch (c) {
@@ -689,9 +729,10 @@ wsemul_vt100_output_scs96_percent(struct wsemul_vt100_emuldata *edp, u_char c)
}
edp->state = VT100_EMUL_STATE_NORMAL;
+ return 0;
}
-void
+int
wsemul_vt100_output_esc_spc(struct wsemul_vt100_emuldata *edp, u_char c)
{
switch (c) {
@@ -709,18 +750,20 @@ wsemul_vt100_output_esc_spc(struct wsemul_vt100_emuldata *edp, u_char c)
}
edp->state = VT100_EMUL_STATE_NORMAL;
+ return 0;
}
-void
+int
wsemul_vt100_output_string(struct wsemul_vt100_emuldata *edp, u_char c)
{
if (edp->dcstype && edp->dcspos < DCS_MAXLEN)
edp->dcsarg[edp->dcspos++] = c;
edp->state = VT100_EMUL_STATE_STRING;
+ return 0;
}
-void
+int
wsemul_vt100_output_string_esc(struct wsemul_vt100_emuldata *edp, u_char c)
{
if (c == '\\') { /* ST complete */
@@ -728,9 +771,11 @@ wsemul_vt100_output_string_esc(struct wsemul_vt100_emuldata *edp, u_char c)
edp->state = VT100_EMUL_STATE_NORMAL;
} else
edp->state = VT100_EMUL_STATE_STRING;
+
+ return 0;
}
-void
+int
wsemul_vt100_output_dcs(struct wsemul_vt100_emuldata *edp, u_char c)
{
u_int newstate = VT100_EMUL_STATE_DCS;
@@ -777,9 +822,10 @@ wsemul_vt100_output_dcs(struct wsemul_vt100_emuldata *edp, u_char c)
}
edp->state = newstate;
+ return 0;
}
-void
+int
wsemul_vt100_output_dcs_dollar(struct wsemul_vt100_emuldata *edp, u_char c)
{
switch (c) {
@@ -817,21 +863,29 @@ wsemul_vt100_output_dcs_dollar(struct wsemul_vt100_emuldata *edp, u_char c)
}
edp->state = VT100_EMUL_STATE_STRING;
+ return 0;
}
-void
+int
wsemul_vt100_output_esc_hash(struct wsemul_vt100_emuldata *edp, u_char c)
{
int i;
+ int rc = 0;
switch (c) {
case '5': /* DECSWL single width, single height */
if (edp->dblwid != NULL && edp->dw != 0) {
- for (i = 0; i < edp->ncols / 2; i++)
- (*edp->emulops->copycols)(edp->emulcookie,
- edp->crow, 2 * i, i, 1);
- (*edp->emulops->erasecols)(edp->emulcookie, edp->crow,
- i, edp->ncols - i, edp->bkgdattr);
+ for (i = 0; i < edp->ncols / 2; i++) {
+ WSEMULOP(rc, edp, &edp->abortstate, copycols,
+ (edp->emulcookie, edp->crow, 2 * i, i, 1));
+ if (rc != 0)
+ return rc;
+ }
+ WSEMULOP(rc, edp, &edp->abortstate, erasecols,
+ (edp->emulcookie, edp->crow, i, edp->ncols - i,
+ edp->bkgdattr));
+ if (rc != 0)
+ return rc;
edp->dblwid[edp->crow] = 0;
edp->dw = 0;
}
@@ -840,12 +894,19 @@ wsemul_vt100_output_esc_hash(struct wsemul_vt100_emuldata *edp, u_char c)
case '3': /* DECDHL double width, double height, top half */
case '4': /* DECDHL double width, double height, bottom half */
if (edp->dblwid != NULL && edp->dw == 0) {
- for (i = edp->ncols / 2 - 1; i >= 0; i--)
- (*edp->emulops->copycols)(edp->emulcookie,
- edp->crow, i, 2 * i, 1);
- for (i = 0; i < edp->ncols / 2; i++)
- (*edp->emulops->erasecols)(edp->emulcookie,
- edp->crow, 2 * i + 1, 1, edp->bkgdattr);
+ for (i = edp->ncols / 2 - 1; i >= 0; i--) {
+ WSEMULOP(rc, edp, &edp->abortstate, copycols,
+ (edp->emulcookie, edp->crow, i, 2 * i, 1));
+ if (rc != 0)
+ return rc;
+ }
+ for (i = 0; i < edp->ncols / 2; i++) {
+ WSEMULOP(rc, edp, &edp->abortstate, erasecols,
+ (edp->emulcookie, edp->crow, 2 * i + 1, 1,
+ edp->bkgdattr));
+ if (rc != 0)
+ return rc;
+ }
edp->dblwid[edp->crow] = 1;
edp->dw = 1;
if (edp->ccol > (edp->ncols >> 1) - 1)
@@ -855,9 +916,12 @@ wsemul_vt100_output_esc_hash(struct wsemul_vt100_emuldata *edp, u_char c)
case '8': { /* DECALN */
int i, j;
for (i = 0; i < edp->nrows; i++)
- for (j = 0; j < edp->ncols; j++)
- (*edp->emulops->putchar)(edp->emulcookie, i, j,
- 'E', edp->curattr);
+ for (j = 0; j < edp->ncols; j++) {
+ WSEMULOP(rc, edp, &edp->abortstate, putchar,
+ (edp->emulcookie, i, j, 'E', edp->curattr));
+ if (rc != 0)
+ return rc;
+ }
}
edp->ccol = 0;
edp->crow = 0;
@@ -870,12 +934,15 @@ wsemul_vt100_output_esc_hash(struct wsemul_vt100_emuldata *edp, u_char c)
}
edp->state = VT100_EMUL_STATE_NORMAL;
+ return 0;
}
-void
+int
wsemul_vt100_output_csi(struct wsemul_vt100_emuldata *edp, u_char c)
{
u_int newstate = VT100_EMUL_STATE_CSI;
+ int oargs;
+ int rc = 0;
switch (c) {
case '0': case '1': case '2': case '3': case '4':
@@ -900,19 +967,24 @@ wsemul_vt100_output_csi(struct wsemul_vt100_emuldata *edp, u_char c)
edp->modif2 = c;
break;
default: /* end of escape sequence */
- edp->nargs++;
+ oargs = edp->nargs++;
if (edp->nargs > VT100_EMUL_NARGS) {
#ifdef VT100_DEBUG
printf("vt100: too many arguments\n");
#endif
edp->nargs = VT100_EMUL_NARGS;
}
- wsemul_vt100_handle_csi(edp, c);
+ rc = wsemul_vt100_handle_csi(edp, c);
+ if (rc != 0) {
+ edp->nargs = oargs;
+ return rc;
+ }
newstate = VT100_EMUL_STATE_NORMAL;
break;
}
edp->state = newstate;
+ return 0;
}
u_int
@@ -924,44 +996,99 @@ wsemul_vt100_output(void *cookie, const u_char *data, u_int count, int kernel)
#ifdef JUMP_SCROLL
int lines;
#endif
+ int rc = 0;
#ifdef DIAGNOSTIC
if (kernel && !edp->console)
panic("wsemul_vt100_output: kernel output, not console");
#endif
- if (edp->flags & VTFL_CURSORON)
- (*edp->emulops->cursor)(edp->emulcookie, 0,
- edp->crow, edp->ccol << edp->dw);
+ switch (edp->abortstate.state) {
+ case ABORT_FAILED_CURSOR:
+ /*
+ * If we could not display the cursor back, we pretended not
+ * having been able to display the last character. But this
+ * is a lie, so compensate here.
+ */
+ data++, count--;
+ processed++;
+ wsemul_reset_abortstate(&edp->abortstate);
+ break;
+ case ABORT_OK:
+ /* remove cursor image if visible */
+ if (edp->flags & VTFL_CURSORON) {
+ rc = (*edp->emulops->cursor)
+ (edp->emulcookie, 0, edp->crow,
+ edp->ccol << edp->dw);
+ if (rc != 0)
+ return 0;
+ }
+ break;
+ default:
+ break;
+ }
for (; count > 0; data++, count--) {
#ifdef JUMP_SCROLL
- /*
- * If we are at the bottom of the scrolling area, count
- * newlines until an escape sequence appears.
- */
- if ((edp->state == VT100_EMUL_STATE_NORMAL || kernel) &&
- ROWS_BELOW == 0)
- lines = wsemul_vt100_jump_scroll(edp, data,
- count, kernel);
- else
+ switch (edp->abortstate.state) {
+ case ABORT_FAILED_JUMP_SCROLL:
+ /*
+ * If we failed a previous jump scroll attempt, we
+ * need to try to resume it with the same distance.
+ * We can not recompute it since there might be more
+ * bytes in the tty ring, causing a different result.
+ */
+ lines = edp->abortstate.lines;
+ break;
+ case ABORT_OK:
+ /*
+ * If we are at the bottom of the scrolling area, count
+ * newlines until an escape sequence appears.
+ */
+ if ((edp->state == VT100_EMUL_STATE_NORMAL || kernel) &&
+ ROWS_BELOW == 0)
+ lines = wsemul_vt100_jump_scroll(edp, data,
+ count, kernel);
+ else
+ lines = 0;
+ break;
+ default:
+ /*
+ * If we are recovering a non-scrolling failure,
+ * do not try to scroll yet.
+ */
lines = 0;
+ break;
+ }
if (lines > 1) {
- wsemul_vt100_scrollup(edp, lines);
+ wsemul_resume_abort(&edp->abortstate);
+ rc = wsemul_vt100_scrollup(edp, lines);
+ if (rc != 0) {
+ wsemul_abort_jump_scroll(&edp->abortstate,
+ lines);
+ return processed;
+ }
+ wsemul_reset_abortstate(&edp->abortstate);
edp->crow -= lines;
}
#endif
+ wsemul_resume_abort(&edp->abortstate);
+
c = *data;
if ((c & 0x7f) < 0x20) {
- wsemul_vt100_output_c0c1(edp, c, kernel);
+ rc = wsemul_vt100_output_c0c1(edp, c, kernel);
+ if (rc != 0)
+ break;
processed++;
continue;
}
if (edp->state == VT100_EMUL_STATE_NORMAL || kernel) {
- wsemul_vt100_output_normal(edp, c, kernel);
+ rc = wsemul_vt100_output_normal(edp, c, kernel);
+ if (rc != 0)
+ break;
processed++;
continue;
}
@@ -969,13 +1096,34 @@ wsemul_vt100_output(void *cookie, const u_char *data, u_int count, int kernel)
if (edp->state > nitems(vt100_output))
panic("wsemul_vt100: invalid state %d", edp->state);
#endif
- vt100_output[edp->state - 1](edp, c);
+ rc = vt100_output[edp->state - 1](edp, c);
+ if (rc != 0)
+ break;
processed++;
}
- if (edp->flags & VTFL_CURSORON)
- (*edp->emulops->cursor)(edp->emulcookie, 1,
- edp->crow, edp->ccol << edp->dw);
+ if (rc != 0)
+ wsemul_abort_other(&edp->abortstate);
+ else {
+ /* put cursor image back if visible */
+ if (edp->flags & VTFL_CURSORON) {
+ rc = (*edp->emulops->cursor)
+ (edp->emulcookie, 1, edp->crow,
+ edp->ccol << edp->dw);
+ if (rc != 0) {
+ /*
+ * Fail the last character output, remembering
+ * that only the cursor operation really needs
+ * to be done.
+ */
+ wsemul_abort_cursor(&edp->abortstate);
+ processed--;
+ }
+ }
+ }
+
+ if (rc == 0)
+ wsemul_reset_abortstate(&edp->abortstate);
return processed;
}
diff --git a/sys/dev/wscons/wsemul_vt100_keys.c b/sys/dev/wscons/wsemul_vt100_keys.c
index eac920de4cf..035088c4128 100644
--- a/sys/dev/wscons/wsemul_vt100_keys.c
+++ b/sys/dev/wscons/wsemul_vt100_keys.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: wsemul_vt100_keys.c,v 1.4 2007/11/27 16:37:27 miod Exp $ */
+/* $OpenBSD: wsemul_vt100_keys.c,v 1.5 2009/09/05 14:49:20 miod Exp $ */
/* $NetBSD: wsemul_vt100_keys.c,v 1.3 1999/04/22 20:06:02 mycroft Exp $ */
/*
@@ -30,9 +30,11 @@
#include <sys/param.h>
#include <sys/systm.h>
+#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wsdisplayvar.h>
#include <dev/wscons/wsksymvar.h>
#include <dev/wscons/wsksymdef.h>
+#include <dev/wscons/wsemulvar.h>
#include <dev/wscons/wsemul_vt100var.h>
static const char *vt100_fkeys[] = {
diff --git a/sys/dev/wscons/wsemul_vt100_subr.c b/sys/dev/wscons/wsemul_vt100_subr.c
index cdbd558d242..f34c9b28651 100644
--- a/sys/dev/wscons/wsemul_vt100_subr.c
+++ b/sys/dev/wscons/wsemul_vt100_subr.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: wsemul_vt100_subr.c,v 1.16 2009/09/05 13:43:58 miod Exp $ */
+/* $OpenBSD: wsemul_vt100_subr.c,v 1.17 2009/09/05 14:49:20 miod Exp $ */
/* $NetBSD: wsemul_vt100_subr.c,v 1.7 2000/04/28 21:56:16 mycroft Exp $ */
/*
@@ -39,7 +39,7 @@
int vt100_selectattribute(struct wsemul_vt100_emuldata *, int, int, int,
long *, long *);
int vt100_ansimode(struct wsemul_vt100_emuldata *, int, int);
-void vt100_decmode(struct wsemul_vt100_emuldata *, int, int);
+int vt100_decmode(struct wsemul_vt100_emuldata *, int, int);
#define VTMODE_SET 33
#define VTMODE_RESET 44
#define VTMODE_REPORT 55
@@ -47,86 +47,119 @@ void vt100_decmode(struct wsemul_vt100_emuldata *, int, int);
/*
* scroll up within scrolling region
*/
-void
+int
wsemul_vt100_scrollup(struct wsemul_vt100_emuldata *edp, int n)
{
int help;
+ int rc;
if (n > edp->scrreg_nrows)
n = edp->scrreg_nrows;
help = edp->scrreg_nrows - n;
if (help > 0) {
- (*edp->emulops->copyrows)(edp->emulcookie,
- edp->scrreg_startrow + n, edp->scrreg_startrow, help);
- if (edp->dblwid) /* XXX OVERLAPS */
- bcopy(&edp->dblwid[edp->scrreg_startrow + n],
- &edp->dblwid[edp->scrreg_startrow], help);
+ WSEMULOP(rc, edp, &edp->abortstate, copyrows,
+ (edp->emulcookie, edp->scrreg_startrow + n,
+ edp->scrreg_startrow, help));
+ if (rc != 0)
+ return rc;
}
- (*edp->emulops->eraserows)(edp->emulcookie,
- edp->scrreg_startrow + help, n, edp->bkgdattr);
- if (edp->dblwid)
+ WSEMULOP(rc, edp, &edp->abortstate, eraserows,
+ (edp->emulcookie, edp->scrreg_startrow + help, n, edp->bkgdattr));
+ if (rc != 0)
+ return rc;
+ if (edp->dblwid) {
+ if (help > 0)
+ ovbcopy(&edp->dblwid[edp->scrreg_startrow + n],
+ &edp->dblwid[edp->scrreg_startrow], help);
memset(&edp->dblwid[edp->scrreg_startrow + help], 0, n);
+ }
CHECK_DW;
+
+ return 0;
}
/*
* scroll down within scrolling region
*/
-void
+int
wsemul_vt100_scrolldown(struct wsemul_vt100_emuldata *edp, int n)
{
int help;
+ int rc;
if (n > edp->scrreg_nrows)
n = edp->scrreg_nrows;
help = edp->scrreg_nrows - n;
if (help > 0) {
- (*edp->emulops->copyrows)(edp->emulcookie,
- edp->scrreg_startrow, edp->scrreg_startrow + n, help);
- if (edp->dblwid) /* XXX OVERLAPS */
- bcopy(&edp->dblwid[edp->scrreg_startrow],
- &edp->dblwid[edp->scrreg_startrow + n], help);
+ WSEMULOP(rc, edp, &edp->abortstate, copyrows,
+ (edp->emulcookie, edp->scrreg_startrow,
+ edp->scrreg_startrow + n, help));
+ if (rc != 0)
+ return rc;
}
- (*edp->emulops->eraserows)(edp->emulcookie, edp->scrreg_startrow, n,
- edp->bkgdattr);
- if (edp->dblwid)
+ WSEMULOP(rc, edp, &edp->abortstate, eraserows,
+ (edp->emulcookie, edp->scrreg_startrow, n, edp->bkgdattr));
+ if (rc != 0)
+ return rc;
+ if (edp->dblwid) {
+ if (help > 0)
+ ovbcopy(&edp->dblwid[edp->scrreg_startrow],
+ &edp->dblwid[edp->scrreg_startrow + n], help);
memset(&edp->dblwid[edp->scrreg_startrow], 0, n);
+ }
CHECK_DW;
+
+ return 0;
}
/*
* erase in display
*/
-void
+int
wsemul_vt100_ed(struct wsemul_vt100_emuldata *edp, int arg)
{
int n;
+ int rc;
switch (arg) {
case 0: /* cursor to end */
- ERASECOLS(edp->ccol, COLS_LEFT + 1, edp->bkgdattr);
+ WSEMULOP(rc, edp, &edp->abortstate, erasecols,
+ ERASECOLS(edp->ccol, COLS_LEFT + 1, edp->bkgdattr));
+ if (rc != 0)
+ break;
n = edp->nrows - edp->crow - 1;
if (n > 0) {
- (*edp->emulops->eraserows)(edp->emulcookie,
- edp->crow + 1, n, edp->bkgdattr);
+ WSEMULOP(rc, edp, &edp->abortstate, eraserows,
+ (edp->emulcookie, edp->crow + 1, n, edp->bkgdattr));
+ if (rc != 0)
+ break;
if (edp->dblwid)
memset(&edp->dblwid[edp->crow + 1], 0, n);
}
break;
case 1: /* beginning to cursor */
if (edp->crow > 0) {
- (*edp->emulops->eraserows)(edp->emulcookie,
- 0, edp->crow, edp->bkgdattr);
- if (edp->dblwid)
+ WSEMULOP(rc, edp, &edp->abortstate, eraserows,
+ (edp->emulcookie, 0, edp->crow, edp->bkgdattr));
+ if (rc != 0)
+ break;
+ }
+ WSEMULOP(rc, edp, &edp->abortstate, erasecols,
+ ERASECOLS(0, edp->ccol + 1, edp->bkgdattr));
+ if (rc != 0)
+ break;
+ if (edp->dblwid) {
+ if (edp->crow > 0)
memset(&edp->dblwid[0], 0, edp->crow);
}
- ERASECOLS(0, edp->ccol + 1, edp->bkgdattr);
break;
case 2: /* complete display */
- (*edp->emulops->eraserows)(edp->emulcookie,
- 0, edp->nrows, edp->bkgdattr);
+ WSEMULOP(rc, edp, &edp->abortstate, eraserows,
+ (edp->emulcookie, 0, edp->nrows, edp->bkgdattr));
+ if (rc != 0)
+ break;
if (edp->dblwid)
memset(&edp->dblwid[0], 0, edp->nrows);
break;
@@ -134,44 +167,58 @@ wsemul_vt100_ed(struct wsemul_vt100_emuldata *edp, int arg)
#ifdef VT100_PRINTUNKNOWN
printf("ed(%d) unknown\n", arg);
#endif
+ rc = 0;
break;
}
+ if (rc != 0)
+ return rc;
+
CHECK_DW;
+
+ return 0;
}
/*
* erase in line
*/
-void
+int
wsemul_vt100_el(struct wsemul_vt100_emuldata *edp, int arg)
{
+ int rc;
+
switch (arg) {
case 0: /* cursor to end */
- ERASECOLS(edp->ccol, COLS_LEFT + 1, edp->bkgdattr);
+ WSEMULOP(rc, edp, &edp->abortstate, erasecols,
+ ERASECOLS(edp->ccol, COLS_LEFT + 1, edp->bkgdattr));
break;
case 1: /* beginning to cursor */
- ERASECOLS(0, edp->ccol + 1, edp->bkgdattr);
+ WSEMULOP(rc, edp, &edp->abortstate, erasecols,
+ ERASECOLS(0, edp->ccol + 1, edp->bkgdattr));
break;
case 2: /* complete line */
- (*edp->emulops->erasecols)(edp->emulcookie, edp->crow,
- 0, edp->ncols, edp->bkgdattr);
+ WSEMULOP(rc, edp, &edp->abortstate, erasecols,
+ (edp->emulcookie, edp->crow, 0, edp->ncols, edp->bkgdattr));
break;
default:
#ifdef VT100_PRINTUNKNOWN
printf("el(%d) unknown\n", arg);
#endif
+ rc = 0;
break;
}
+
+ return rc;
}
/*
* handle commands after CSI (ESC[)
*/
-void
+int
wsemul_vt100_handle_csi(struct wsemul_vt100_emuldata *edp, u_char c)
{
int n, help, flags, fgcol, bgcol;
long attr, bkgdattr;
+ int rc = 0;
#define A3(a, b, c) (((a) << 16) | ((b) << 8) | (c))
switch (A3(edp->modif1, edp->modif2, c)) {
@@ -182,33 +229,39 @@ wsemul_vt100_handle_csi(struct wsemul_vt100_emuldata *edp, u_char c)
case A3('\0', '\0', 'J'): /* ED selective erase in display */
case A3('?', '\0', 'J'): /* DECSED selective erase in display */
- wsemul_vt100_ed(edp, ARG(0));
+ rc = wsemul_vt100_ed(edp, ARG(0));
break;
case A3('\0', '\0', 'K'): /* EL selective erase in line */
case A3('?', '\0', 'K'): /* DECSEL selective erase in line */
- wsemul_vt100_el(edp, ARG(0));
+ rc = wsemul_vt100_el(edp, ARG(0));
break;
case A3('\0', '\0', 'h'): /* SM */
for (n = 0; n < edp->nargs; n++)
vt100_ansimode(edp, ARG(n), VTMODE_SET);
break;
case A3('?', '\0', 'h'): /* DECSM */
- for (n = 0; n < edp->nargs; n++)
- vt100_decmode(edp, ARG(n), VTMODE_SET);
+ for (n = 0; n < edp->nargs; n++) {
+ rc = vt100_decmode(edp, ARG(n), VTMODE_SET);
+ if (rc != 0)
+ break;
+ }
break;
case A3('\0', '\0', 'l'): /* RM */
for (n = 0; n < edp->nargs; n++)
vt100_ansimode(edp, ARG(n), VTMODE_RESET);
break;
case A3('?', '\0', 'l'): /* DECRM */
- for (n = 0; n < edp->nargs; n++)
- vt100_decmode(edp, ARG(n), VTMODE_RESET);
+ for (n = 0; n < edp->nargs; n++) {
+ rc = vt100_decmode(edp, ARG(n), VTMODE_RESET);
+ if (rc != 0)
+ break;
+ }
break;
case A3('\0', '$', 'p'): /* DECRQM request mode ANSI */
vt100_ansimode(edp, ARG(0), VTMODE_REPORT);
break;
case A3('?', '$', 'p'): /* DECRQM request mode DEC */
- vt100_decmode(edp, ARG(0), VTMODE_REPORT);
+ rc = vt100_decmode(edp, ARG(0), VTMODE_REPORT);
break;
case A3('\0', '\0', 'i'): /* MC printer controller mode */
case A3('?', '\0', 'i'): /* MC printer controller mode */
@@ -336,16 +389,17 @@ wsemul_vt100_handle_csi(struct wsemul_vt100_emuldata *edp, u_char c)
break;
}
break;
+ /* gratuitous { for brace matching with the next line */
case A2('$', '}'): /* DECSASD select active status display */
switch (ARG(0)) {
case 0: /* main display */
case 1: /* status line */
-#ifdef VT100_PRINTNOTIMPL
+#ifdef VT100_PRINTNOTIMPL /* { */
printf("CSI%d$} ignored\n", ARG(0));
#endif
break;
default:
-#ifdef VT100_PRINTUNKNOWN
+#ifdef VT100_PRINTUNKNOWN /* { */
printf("CSI%d$} unknown\n", ARG(0));
#endif
break;
@@ -376,9 +430,14 @@ wsemul_vt100_handle_csi(struct wsemul_vt100_emuldata *edp, u_char c)
case '@': /* ICH insert character VT300 only */
n = min(DEF1_ARG(0), COLS_LEFT + 1);
help = NCOLS - (edp->ccol + n);
- if (help > 0)
- COPYCOLS(edp->ccol, edp->ccol + n, help);
- ERASECOLS(edp->ccol, n, edp->bkgdattr);
+ if (help > 0) {
+ WSEMULOP(rc, edp, &edp->abortstate, copycols,
+ COPYCOLS(edp->ccol, edp->ccol + n, help));
+ if (rc != 0)
+ break;
+ }
+ WSEMULOP(rc, edp, &edp->abortstate, erasecols,
+ ERASECOLS(edp->ccol, n, edp->bkgdattr));
break;
case 'A': /* CUU */
edp->crow -= min(DEF1_ARG(0), ROWS_ABOVE);
@@ -417,9 +476,9 @@ wsemul_vt100_handle_csi(struct wsemul_vt100_emuldata *edp, u_char c)
edp->scrreg_nrows -= ROWS_ABOVE;
edp->scrreg_startrow = edp->crow;
if (c == 'L')
- wsemul_vt100_scrolldown(edp, n);
+ rc = wsemul_vt100_scrolldown(edp, n);
else
- wsemul_vt100_scrollup(edp, n);
+ rc = wsemul_vt100_scrollup(edp, n);
edp->scrreg_startrow = savscrstartrow;
edp->scrreg_nrows = savscrnrows;
}
@@ -427,13 +486,19 @@ wsemul_vt100_handle_csi(struct wsemul_vt100_emuldata *edp, u_char c)
case 'P': /* DCH delete character */
n = min(DEF1_ARG(0), COLS_LEFT + 1);
help = NCOLS - (edp->ccol + n);
- if (help > 0)
- COPYCOLS(edp->ccol + n, edp->ccol, help);
- ERASECOLS(NCOLS - n, n, edp->bkgdattr);
+ if (help > 0) {
+ WSEMULOP(rc, edp, &edp->abortstate, copycols,
+ COPYCOLS(edp->ccol + n, edp->ccol, help));
+ if (rc != 0)
+ break;
+ }
+ WSEMULOP(rc, edp, &edp->abortstate, erasecols,
+ ERASECOLS(NCOLS - n, n, edp->bkgdattr));
break;
case 'X': /* ECH erase character */
n = min(DEF1_ARG(0), COLS_LEFT + 1);
- ERASECOLS(edp->ccol, n, edp->bkgdattr);
+ WSEMULOP(rc, edp, &edp->abortstate, erasecols,
+ ERASECOLS(edp->ccol, n, edp->bkgdattr));
break;
case 'c': /* DA primary */
if (ARG(0) == 0)
@@ -468,7 +533,7 @@ wsemul_vt100_handle_csi(struct wsemul_vt100_emuldata *edp, u_char c)
edp->attrflags = 0;
edp->fgcol = WSCOL_WHITE;
edp->bgcol = WSCOL_BLACK;
- return;
+ return 0;
}
flags = 0;
fgcol = WSCOL_WHITE;
@@ -578,7 +643,7 @@ wsemul_vt100_handle_csi(struct wsemul_vt100_emuldata *edp, u_char c)
n = min(DEFx_ARG(1, edp->nrows), edp->nrows) - help;
if (n < 2) {
/* minimal scrolling region has 2 lines */
- return;
+ return 0;
} else {
edp->scrreg_startrow = help;
edp->scrreg_nrows = n;
@@ -605,6 +670,8 @@ wsemul_vt100_handle_csi(struct wsemul_vt100_emuldata *edp, u_char c)
#endif
break;
}
+
+ return rc;
}
/*
@@ -765,13 +832,14 @@ vt100_ansimode(struct wsemul_vt100_emuldata *edp, int nr, int op)
return (res);
}
-void
+int
vt100_decmode(struct wsemul_vt100_emuldata *edp, int nr, int op)
{
#if 0 /* res unused... return it by reference if ever necessary */
int res = 0; /* default: unknown */
#endif
int flags = edp->flags;
+ int rc = 0;
switch (nr) {
case 1: /* DECCKM application/nomal cursor keys */
@@ -825,8 +893,9 @@ vt100_decmode(struct wsemul_vt100_emuldata *edp, int nr, int op)
else if (op == VTMODE_RESET)
flags &= ~VTFL_CURSORON;
if (flags != edp->flags)
- (*edp->emulops->cursor)(edp->emulcookie,
- flags & VTFL_CURSORON, edp->crow, edp->ccol);
+ WSEMULOP(rc, edp, &edp->abortstate, cursor,
+ (edp->emulcookie, flags & VTFL_CURSORON, edp->crow,
+ edp->ccol));
#if 0
res = ((flags & VTFL_CURSORON) ? 1 : 2);
#endif
@@ -852,4 +921,6 @@ vt100_decmode(struct wsemul_vt100_emuldata *edp, int nr, int op)
break;
}
edp->flags = flags;
+
+ return rc;
}
diff --git a/sys/dev/wscons/wsemul_vt100var.h b/sys/dev/wscons/wsemul_vt100var.h
index f72e734761d..3926eb734fc 100644
--- a/sys/dev/wscons/wsemul_vt100var.h
+++ b/sys/dev/wscons/wsemul_vt100var.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: wsemul_vt100var.h,v 1.8 2007/11/27 16:37:27 miod Exp $ */
+/* $OpenBSD: wsemul_vt100var.h,v 1.9 2009/09/05 14:49:20 miod Exp $ */
/* $NetBSD: wsemul_vt100var.h,v 1.5 2000/04/28 21:56:17 mycroft Exp $ */
/*
@@ -31,6 +31,7 @@
struct wsemul_vt100_emuldata {
const struct wsdisplay_emulops *emulops;
+ struct wsemul_abortstate abortstate;
void *emulcookie;
int scrcapabilities;
u_int nrows, ncols, crow, ccol;
@@ -106,10 +107,10 @@ struct wsemul_vt100_emuldata {
} while (0)
#define NCOLS (edp->ncols >> edp->dw)
#define COLS_LEFT (NCOLS - edp->ccol - 1)
-#define COPYCOLS(f, t, n) (*edp->emulops->copycols)(edp->emulcookie, \
- edp->crow, (f) << edp->dw, (t) << edp->dw, (n) << edp->dw)
-#define ERASECOLS(f, n, a) (*edp->emulops->erasecols)(edp->emulcookie, \
- edp->crow, (f) << edp->dw, (n) << edp->dw, a)
+#define COPYCOLS(f, t, n) (edp->emulcookie, edp->crow, (f) << edp->dw, \
+ (t) << edp->dw, (n) << edp->dw)
+#define ERASECOLS(f, n, a) (edp->emulcookie, edp->crow, (f) << edp->dw, \
+ (n) << edp->dw, a)
/*
* response to primary DA request
@@ -128,11 +129,11 @@ struct wsemul_vt100_emuldata {
#define WSEMUL_VT_ID2 "\033[>24;20;0c"
void wsemul_vt100_reset(struct wsemul_vt100_emuldata *);
-void wsemul_vt100_scrollup(struct wsemul_vt100_emuldata *, int);
-void wsemul_vt100_scrolldown(struct wsemul_vt100_emuldata *, int);
-void wsemul_vt100_ed(struct wsemul_vt100_emuldata *, int);
-void wsemul_vt100_el(struct wsemul_vt100_emuldata *, int);
-void wsemul_vt100_handle_csi(struct wsemul_vt100_emuldata *, u_char);
+int wsemul_vt100_scrollup(struct wsemul_vt100_emuldata *, int);
+int wsemul_vt100_scrolldown(struct wsemul_vt100_emuldata *, int);
+int wsemul_vt100_ed(struct wsemul_vt100_emuldata *, int);
+int wsemul_vt100_el(struct wsemul_vt100_emuldata *, int);
+int wsemul_vt100_handle_csi(struct wsemul_vt100_emuldata *, u_char);
void wsemul_vt100_handle_dcs(struct wsemul_vt100_emuldata *);
int wsemul_vt100_translate(void *cookie, keysym_t, const char **);
diff --git a/sys/dev/wscons/wsemulvar.h b/sys/dev/wscons/wsemulvar.h
index a4370ce7c69..23358af2ae4 100644
--- a/sys/dev/wscons/wsemulvar.h
+++ b/sys/dev/wscons/wsemulvar.h
@@ -1,7 +1,22 @@
-/* $OpenBSD: wsemulvar.h,v 1.11 2009/09/05 14:30:24 miod Exp $ */
+/* $OpenBSD: wsemulvar.h,v 1.12 2009/09/05 14:49:20 miod Exp $ */
/* $NetBSD: wsemulvar.h,v 1.6 1999/01/17 15:46:15 drochner Exp $ */
/*
+ * Copyright (c) 2009 Miodrag Vallat.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
* Copyright (c) 1996, 1997 Christopher G. Demetriou. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -31,6 +46,8 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#ifdef _KERNEL
+
struct device;
struct wsdisplay_emulops;
@@ -65,3 +82,98 @@ const struct wsemul_ops *wsemul_pick(const char *);
*/
void wsdisplay_emulbell(void *v);
void wsdisplay_emulinput(void *v, const u_char *, u_int);
+
+/*
+ * emulops failure abort/recovery state
+ *
+ * The tty layer needs a character output to be atomic. Since this may
+ * expand to multiple emulops operations, which may fail, it is necessary
+ * for each emulation code to keep state of its current processing, so
+ * that if an operation fails, the whole character from the tty layer is
+ * reported as not having been output, while it has in fact been partly
+ * processed.
+ *
+ * When the tty layer will try to retransmit the character, this state
+ * information is used to not retrig the emulops which have been issued
+ * succesfully already.
+ *
+ * In order to make things more confusing, there is a particular failure
+ * case, when all characters have been processed successfully, but
+ * displaying the cursor image fails.
+ *
+ * Since there might not be tty output in a while, we need to report
+ * failure, so we pretend not having been able to issue the last character.
+ * When the tty layer tries again to display this character (really to get
+ * the cursor image back), it will directly be skipped. This is done with
+ * a special state value.
+ */
+
+struct wsemul_abortstate {
+ enum {
+ ABORT_OK,
+ ABORT_FAILED_CURSOR,
+ ABORT_FAILED_JUMP_SCROLL,
+ ABORT_FAILED_OTHER
+ } state;
+ int skip; /* emulops to skip before reaching resume point */
+ int done; /* emulops completed */
+ int lines; /* jump scroll lines */
+};
+
+/* start character processing, assuming cursor or jump scroll failure condition
+ has been taken care of */
+static __inline__ void
+wsemul_resume_abort(struct wsemul_abortstate *was)
+{
+ was->state = ABORT_OK;
+ was->done = 0;
+}
+
+/* register processing failure points */
+static __inline__ void
+wsemul_abort_cursor(struct wsemul_abortstate *was)
+{
+ was->state = ABORT_FAILED_CURSOR;
+}
+
+static __inline__ void
+wsemul_abort_jump_scroll(struct wsemul_abortstate *was, int lines)
+{
+ was->state = ABORT_FAILED_JUMP_SCROLL;
+ was->skip = was->done;
+ was->lines = lines;
+}
+
+static __inline__ void
+wsemul_abort_other(struct wsemul_abortstate *was)
+{
+ was->state = ABORT_FAILED_OTHER;
+ was->skip = was->done;
+}
+
+/* initialize abortstate structure */
+static __inline__ void
+wsemul_reset_abortstate(struct wsemul_abortstate *was)
+{
+ was->state = ABORT_OK;
+ was->skip = 0;
+ /* was->done = 0; */
+}
+
+/*
+ * Wrapper macro to handle failing emulops calls consistently.
+ */
+
+#define WSEMULOP(rc, edp, was, rutin, args) \
+do { \
+ if ((was)->skip != 0) { \
+ (was)->skip--; \
+ (rc) = 0; \
+ } else { \
+ (rc) = (*(edp)->emulops->rutin) args ; \
+ } \
+ if ((rc) == 0) \
+ (was)->done++; \
+} while (0)
+
+#endif /* _KERNEL */