xref: /openbsd/sys/dev/wscons/wsemul_vt100.c (revision 7f8d19bf)
1 /* $OpenBSD: wsemul_vt100.c,v 1.48 2024/11/05 08:12:08 miod Exp $ */
2 /* $NetBSD: wsemul_vt100.c,v 1.13 2000/04/28 21:56:16 mycroft Exp $ */
3 
4 /*
5  * Copyright (c) 2007, 2013 Miodrag Vallat.
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice, this permission notice, and the disclaimer below
10  * appear in all copies.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19  */
20 /*
21  * Copyright (c) 1998
22  *	Matthias Drochner.  All rights reserved.
23  *
24  * Redistribution and use in source and binary forms, with or without
25  * modification, are permitted provided that the following conditions
26  * are met:
27  * 1. Redistributions of source code must retain the above copyright
28  *    notice, this list of conditions and the following disclaimer.
29  * 2. Redistributions in binary form must reproduce the above copyright
30  *    notice, this list of conditions and the following disclaimer in the
31  *    documentation and/or other materials provided with the distribution.
32  *
33  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
34  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
35  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
36  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
37  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
38  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
39  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
40  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
41  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
42  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43  *
44  */
45 
46 #include <sys/param.h>
47 #include <sys/systm.h>
48 #include <sys/time.h>
49 #include <sys/malloc.h>
50 #include <sys/fcntl.h>
51 
52 #include <dev/wscons/wscons_features.h>
53 #include <dev/wscons/wsconsio.h>
54 #include <dev/wscons/wsdisplayvar.h>
55 #include <dev/wscons/wsemulvar.h>
56 #include <dev/wscons/wsemul_vt100var.h>
57 #include <dev/wscons/ascii.h>
58 
59 void	*wsemul_vt100_cnattach(const struct wsscreen_descr *, void *,
60 				  int, int, uint32_t);
61 void	*wsemul_vt100_attach(int, const struct wsscreen_descr *,
62 				  void *, int, int, void *, uint32_t);
63 u_int	wsemul_vt100_output(void *, const u_char *, u_int, int);
64 void	wsemul_vt100_detach(void *, u_int *, u_int *);
65 void	wsemul_vt100_resetop(void *, enum wsemul_resetops);
66 
67 const struct wsemul_ops wsemul_vt100_ops = {
68 	"vt100",
69 	wsemul_vt100_cnattach,
70 	wsemul_vt100_attach,
71 	wsemul_vt100_output,
72 	wsemul_vt100_translate,
73 	wsemul_vt100_detach,
74 	wsemul_vt100_resetop
75 };
76 
77 struct wsemul_vt100_emuldata wsemul_vt100_console_emuldata;
78 
79 void	wsemul_vt100_init(struct wsemul_vt100_emuldata *,
80 	    const struct wsscreen_descr *, void *, int, int, uint32_t);
81 int	wsemul_vt100_jump_scroll(struct wsemul_vt100_emuldata *,
82 	    const u_char *, u_int, int);
83 int	wsemul_vt100_output_c0c1(struct wsemul_vt100_emuldata *,
84 	    struct wsemul_inputstate *, int);
85 int	wsemul_vt100_nextline(struct wsemul_vt100_emuldata *);
86 
87 typedef int vt100_handler(struct wsemul_vt100_emuldata *, struct
88 	    wsemul_inputstate *, int);
89 vt100_handler
90 	wsemul_vt100_output_esc,
91 	wsemul_vt100_output_csi,
92 	wsemul_vt100_output_scs94,
93 	wsemul_vt100_output_scs94_percent,
94 	wsemul_vt100_output_scs96,
95 	wsemul_vt100_output_scs96_percent,
96 	wsemul_vt100_output_esc_hash,
97 	wsemul_vt100_output_esc_spc,
98 	wsemul_vt100_output_string,
99 	wsemul_vt100_output_string_esc,
100 	wsemul_vt100_output_dcs,
101 	wsemul_vt100_output_dcs_dollar,
102 	wsemul_vt100_output_esc_percent;
103 
104 #define	VT100_EMUL_STATE_NORMAL		0	/* normal processing */
105 #define	VT100_EMUL_STATE_ESC		1	/* got ESC */
106 #define	VT100_EMUL_STATE_CSI		2	/* got CSI (ESC[) */
107 #define	VT100_EMUL_STATE_SCS94		3	/* got ESC{()*+} */
108 #define	VT100_EMUL_STATE_SCS94_PERCENT	4	/* got ESC{()*+}% */
109 #define	VT100_EMUL_STATE_SCS96		5	/* got ESC{-./} */
110 #define	VT100_EMUL_STATE_SCS96_PERCENT	6	/* got ESC{-./}% */
111 #define	VT100_EMUL_STATE_ESC_HASH	7	/* got ESC# */
112 #define	VT100_EMUL_STATE_ESC_SPC	8	/* got ESC<SPC> */
113 #define	VT100_EMUL_STATE_STRING		9	/* waiting for ST (ESC\) */
114 #define	VT100_EMUL_STATE_STRING_ESC	10	/* waiting for ST, got ESC */
115 #define	VT100_EMUL_STATE_DCS		11	/* got DCS (ESC P) */
116 #define	VT100_EMUL_STATE_DCS_DOLLAR	12	/* got DCS<p>$ */
117 #define	VT100_EMUL_STATE_ESC_PERCENT	13	/* got ESC% */
118 
119 vt100_handler *vt100_output[] = {
120 	wsemul_vt100_output_esc,
121 	wsemul_vt100_output_csi,
122 	wsemul_vt100_output_scs94,
123 	wsemul_vt100_output_scs94_percent,
124 	wsemul_vt100_output_scs96,
125 	wsemul_vt100_output_scs96_percent,
126 	wsemul_vt100_output_esc_hash,
127 	wsemul_vt100_output_esc_spc,
128 	wsemul_vt100_output_string,
129 	wsemul_vt100_output_string_esc,
130 	wsemul_vt100_output_dcs,
131 	wsemul_vt100_output_dcs_dollar,
132 	wsemul_vt100_output_esc_percent,
133 };
134 
135 void
wsemul_vt100_init(struct wsemul_vt100_emuldata * edp,const struct wsscreen_descr * type,void * cookie,int ccol,int crow,uint32_t defattr)136 wsemul_vt100_init(struct wsemul_vt100_emuldata *edp,
137     const struct wsscreen_descr *type, void *cookie, int ccol, int crow,
138     uint32_t defattr)
139 {
140 	edp->emulops = type->textops;
141 	edp->emulcookie = cookie;
142 	edp->scrcapabilities = type->capabilities;
143 	edp->nrows = type->nrows;
144 	edp->ncols = type->ncols;
145 	edp->crow = crow;
146 	edp->ccol = ccol;
147 	edp->defattr = defattr;
148 	wsemul_reset_abortstate(&edp->abortstate);
149 }
150 
151 void *
wsemul_vt100_cnattach(const struct wsscreen_descr * type,void * cookie,int ccol,int crow,uint32_t defattr)152 wsemul_vt100_cnattach(const struct wsscreen_descr *type, void *cookie, int ccol,
153     int crow, uint32_t defattr)
154 {
155 	struct wsemul_vt100_emuldata *edp;
156 	int res;
157 
158 	edp = &wsemul_vt100_console_emuldata;
159 	wsemul_vt100_init(edp, type, cookie, ccol, crow, defattr);
160 #ifdef DIAGNOSTIC
161 	edp->console = 1;
162 #endif
163 	edp->cbcookie = NULL;
164 
165 #ifndef WS_KERNEL_FG
166 #define WS_KERNEL_FG WSCOL_WHITE
167 #endif
168 #ifndef WS_KERNEL_BG
169 #define WS_KERNEL_BG WSCOL_BLUE
170 #endif
171 #ifndef WS_KERNEL_COLATTR
172 #define WS_KERNEL_COLATTR 0
173 #endif
174 #ifndef WS_KERNEL_MONOATTR
175 #define WS_KERNEL_MONOATTR 0
176 #endif
177 	if (type->capabilities & WSSCREEN_WSCOLORS)
178 		res = (*edp->emulops->pack_attr)(cookie,
179 		    WS_KERNEL_FG, WS_KERNEL_BG,
180 		    WS_KERNEL_COLATTR | WSATTR_WSCOLORS, &edp->kernattr);
181 	else
182 		res = (*edp->emulops->pack_attr)(cookie, 0, 0,
183 		    WS_KERNEL_MONOATTR, &edp->kernattr);
184 	if (res)
185 		edp->kernattr = defattr;
186 
187 	edp->tabs = NULL;
188 #ifdef HAVE_DOUBLE_WIDTH_HEIGHT
189 	edp->dblwid = NULL;
190 	edp->dw = 0;
191 #endif
192 	edp->dcsarg = NULL;
193 	edp->isolatin1tab = edp->decgraphtab = edp->dectechtab = NULL;
194 	edp->nrctab = NULL;
195 	wsemul_vt100_reset(edp);
196 	return (edp);
197 }
198 
199 void *
wsemul_vt100_attach(int console,const struct wsscreen_descr * type,void * cookie,int ccol,int crow,void * cbcookie,uint32_t defattr)200 wsemul_vt100_attach(int console, const struct wsscreen_descr *type,
201     void *cookie, int ccol, int crow, void *cbcookie, uint32_t defattr)
202 {
203 	struct wsemul_vt100_emuldata *edp;
204 
205 	if (console) {
206 		edp = &wsemul_vt100_console_emuldata;
207 		KASSERT(edp->console == 1);
208 	} else {
209 		edp = malloc(sizeof *edp, M_DEVBUF, M_NOWAIT);
210 		if (edp == NULL)
211 			return (NULL);
212 		wsemul_vt100_init(edp, type, cookie, ccol, crow, defattr);
213 #ifdef DIAGNOSTIC
214 		edp->console = 0;
215 #endif
216 	}
217 	edp->cbcookie = cbcookie;
218 
219 	edp->tabs = malloc(edp->ncols, M_DEVBUF, M_NOWAIT);
220 #ifdef HAVE_DOUBLE_WIDTH_HEIGHT
221 	edp->dblwid = malloc(edp->nrows, M_DEVBUF, M_NOWAIT | M_ZERO);
222 	edp->dw = 0;
223 #endif
224 	edp->dcsarg = malloc(DCS_MAXLEN, M_DEVBUF, M_NOWAIT);
225 	edp->isolatin1tab = malloc(128 * sizeof(u_int), M_DEVBUF, M_NOWAIT);
226 	edp->decgraphtab = malloc(128 * sizeof(u_int), M_DEVBUF, M_NOWAIT);
227 	edp->dectechtab = malloc(128 * sizeof(u_int), M_DEVBUF, M_NOWAIT);
228 	edp->nrctab = malloc(128 * sizeof(u_int), M_DEVBUF, M_NOWAIT);
229 	vt100_initchartables(edp);
230 	wsemul_vt100_reset(edp);
231 	return (edp);
232 }
233 
234 void
wsemul_vt100_detach(void * cookie,u_int * crowp,u_int * ccolp)235 wsemul_vt100_detach(void *cookie, u_int *crowp, u_int *ccolp)
236 {
237 	struct wsemul_vt100_emuldata *edp = cookie;
238 
239 	*crowp = edp->crow;
240 	*ccolp = edp->ccol;
241 #define f(ptr) do { free(ptr, M_DEVBUF, 0); ptr = NULL; } while (0)
242 	f(edp->tabs);
243 #ifdef HAVE_DOUBLE_WIDTH_HEIGHT
244 	f(edp->dblwid);
245 #endif
246 	f(edp->dcsarg);
247 	f(edp->isolatin1tab);
248 	f(edp->decgraphtab);
249 	f(edp->dectechtab);
250 	f(edp->nrctab);
251 #undef f
252 	if (edp != &wsemul_vt100_console_emuldata)
253 		free(edp, M_DEVBUF, sizeof *edp);
254 }
255 
256 void
wsemul_vt100_resetop(void * cookie,enum wsemul_resetops op)257 wsemul_vt100_resetop(void *cookie, enum wsemul_resetops op)
258 {
259 	struct wsemul_vt100_emuldata *edp = cookie;
260 
261 	switch (op) {
262 	case WSEMUL_RESET:
263 		wsemul_vt100_reset(edp);
264 		break;
265 	case WSEMUL_SYNCFONT:
266 		vt100_initchartables(edp);
267 		break;
268 	case WSEMUL_CLEARSCREEN:
269 		(void)wsemul_vt100_ed(edp, 2);
270 		edp->ccol = edp->crow = 0;
271 		(*edp->emulops->cursor)(edp->emulcookie,
272 		    edp->flags & VTFL_CURSORON, 0, 0);
273 		break;
274 	case WSEMUL_CLEARCURSOR:
275 		(*edp->emulops->cursor)(edp->emulcookie, 0, edp->crow,
276 		    edp->ccol);
277 		break;
278 	default:
279 		break;
280 	}
281 }
282 
283 void
wsemul_vt100_reset(struct wsemul_vt100_emuldata * edp)284 wsemul_vt100_reset(struct wsemul_vt100_emuldata *edp)
285 {
286 	int i;
287 
288 	edp->state = VT100_EMUL_STATE_NORMAL;
289 	edp->flags = VTFL_DECAWM | VTFL_CURSORON;
290 	edp->bkgdattr = edp->curattr = edp->defattr;
291 	edp->attrflags = 0;
292 	edp->fgcol = WSCOL_WHITE;
293 	edp->bgcol = WSCOL_BLACK;
294 	edp->scrreg_startrow = 0;
295 	edp->scrreg_nrows = edp->nrows;
296 	if (edp->tabs) {
297 		memset(edp->tabs, 0, edp->ncols);
298 		for (i = 8; i < edp->ncols; i += 8)
299 			edp->tabs[i] = 1;
300 	}
301 	edp->dcspos = 0;
302 	edp->dcstype = 0;
303 	edp->chartab_G[0] = NULL;
304 	edp->chartab_G[1] = edp->nrctab; /* ??? */
305 	edp->chartab_G[2] = edp->isolatin1tab;
306 	edp->chartab_G[3] = edp->isolatin1tab;
307 	edp->chartab0 = 0;
308 	edp->chartab1 = 2;
309 	edp->sschartab = 0;
310 	edp->instate.inchar = 0;
311 	edp->instate.lbound = 0;
312 	edp->instate.mbleft = 0;
313 	edp->instate.last_output = 0;
314 	edp->kstate.inchar = 0;
315 	edp->kstate.lbound = 0;
316 	edp->kstate.mbleft = 0;
317 	edp->kstate.last_output = 0;
318 }
319 
320 /*
321  * Move the cursor to the next line if possible. If the cursor is at
322  * the bottom of the scroll area, then scroll it up. If the cursor is
323  * at the bottom of the screen then don't move it down.
324  */
325 int
wsemul_vt100_nextline(struct wsemul_vt100_emuldata * edp)326 wsemul_vt100_nextline(struct wsemul_vt100_emuldata *edp)
327 {
328 	int rc;
329 
330 	if (ROWS_BELOW == 0) {
331 		/* Bottom of the scroll region. */
332 	  	rc = wsemul_vt100_scrollup(edp, 1);
333 	} else {
334 		if ((edp->crow+1) < edp->nrows)
335 			/* Cursor not at the bottom of the screen. */
336 			edp->crow++;
337 		CHECK_DW;
338 		rc = 0;
339 	}
340 
341 	return rc;
342 }
343 
344 /*
345  * now all the state machine bits
346  */
347 
348 int
wsemul_vt100_output_normal(struct wsemul_vt100_emuldata * edp,struct wsemul_inputstate * instate,int kernel,int count)349 wsemul_vt100_output_normal(struct wsemul_vt100_emuldata *edp,
350     struct wsemul_inputstate *instate, int kernel, int count)
351 {
352 	u_int *ct, dc;
353 	u_char c;
354 	int oldsschartab = edp->sschartab;
355 	int m;
356 	int rc = 0;
357 
358 #ifdef HAVE_UTF8_SUPPORT
359 	if (edp->flags & VTFL_UTF8) {
360 		(*edp->emulops->mapchar)(edp->emulcookie, instate->inchar, &dc);
361 	} else
362 #endif
363 	{
364 		c = instate->inchar & 0xff;
365 		if (c & 0x80) {
366 			c &= 0x7f;
367 			ct = edp->chartab_G[edp->chartab1];
368 		} else {
369 			if (edp->sschartab) {
370 				ct = edp->chartab_G[edp->sschartab];
371 				edp->sschartab = 0;
372 			} else
373 				ct = edp->chartab_G[edp->chartab0];
374 		}
375 		dc = ct ? ct[c] : c;
376 	}
377 
378 	for (m = 0; m < count; m++) {
379 		if ((edp->flags & (VTFL_LASTCHAR | VTFL_DECAWM)) ==
380 		    (VTFL_LASTCHAR | VTFL_DECAWM)) {
381 			rc = wsemul_vt100_nextline(edp);
382 			if (rc != 0)
383 				return rc;
384 			edp->ccol = 0;
385 			edp->flags &= ~VTFL_LASTCHAR;
386 		}
387 
388 		if ((edp->flags & VTFL_INSERTMODE) && COLS_LEFT) {
389 			WSEMULOP(rc, edp, &edp->abortstate, copycols,
390 			    COPYCOLS(edp->ccol, edp->ccol + 1, COLS_LEFT));
391 			if (rc != 0)
392 				break;
393 		}
394 
395 #ifdef HAVE_DOUBLE_WIDTH_HEIGHT
396 		WSEMULOP(rc, edp, &edp->abortstate, putchar,
397 		    (edp->emulcookie, edp->crow, edp->ccol << edp->dw, dc,
398 		     kernel ? edp->kernattr : edp->curattr));
399 #else
400 		WSEMULOP(rc, edp, &edp->abortstate, putchar,
401 		    (edp->emulcookie, edp->crow, edp->ccol, dc,
402 		     kernel ? edp->kernattr : edp->curattr));
403 #endif
404 		if (rc != 0)
405 			break;
406 
407 		if (COLS_LEFT)
408 			edp->ccol++;
409 		else
410 			edp->flags |= VTFL_LASTCHAR;
411 	}
412 
413 	if (rc != 0) {
414 		/* undo potential sschartab update */
415 		edp->sschartab = oldsschartab;
416 
417 		return rc;
418 	}
419 
420 	return 0;
421 }
422 
423 int
wsemul_vt100_output_c0c1(struct wsemul_vt100_emuldata * edp,struct wsemul_inputstate * instate,int kernel)424 wsemul_vt100_output_c0c1(struct wsemul_vt100_emuldata *edp,
425     struct wsemul_inputstate *instate, int kernel)
426 {
427 	u_int n;
428 	int rc = 0;
429 
430 	switch (instate->inchar) {
431 	case ASCII_NUL:
432 	default:
433 		/* ignore */
434 		break;
435 	case ASCII_BEL:
436 		if (edp->state == VT100_EMUL_STATE_STRING) {
437 			/* acts as an equivalent to the ``ESC \'' string end */
438 			wsemul_vt100_handle_dcs(edp);
439 			edp->state = VT100_EMUL_STATE_NORMAL;
440 		} else {
441 			wsdisplay_emulbell(edp->cbcookie);
442 		}
443 		break;
444 	case ASCII_BS:
445 		if (edp->ccol > 0) {
446 			edp->ccol--;
447 			edp->flags &= ~VTFL_LASTCHAR;
448 		}
449 		break;
450 	case ASCII_CR:
451 		edp->ccol = 0;
452 		break;
453 	case ASCII_HT:
454 		if (edp->tabs) {
455 			if (!COLS_LEFT)
456 				break;
457 			for (n = edp->ccol + 1; n < NCOLS - 1; n++)
458 				if (edp->tabs[n])
459 					break;
460 		} else {
461 			n = edp->ccol + min(8 - (edp->ccol & 7), COLS_LEFT);
462 		}
463 		edp->ccol = n;
464 		break;
465 	case ASCII_SO: /* LS1 */
466 		edp->flags &= ~VTFL_UTF8;
467 		edp->chartab0 = 1;
468 		break;
469 	case ASCII_SI: /* LS0 */
470 		edp->flags &= ~VTFL_UTF8;
471 		edp->chartab0 = 0;
472 		break;
473 	case ASCII_ESC:
474 		if (kernel) {
475 			printf("wsemul_vt100_output_c0c1: ESC in kernel "
476 			    "output ignored\n");
477 			break;	/* ignore the ESC */
478 		}
479 
480 		if (edp->state == VT100_EMUL_STATE_STRING) {
481 			/* might be a string end */
482 			edp->state = VT100_EMUL_STATE_STRING_ESC;
483 		} else {
484 			/* XXX cancel current escape sequence */
485 			edp->state = VT100_EMUL_STATE_ESC;
486 		}
487 		break;
488 	case ASCII_CAN:
489 	case ASCII_SUB:
490 		/* cancel current escape sequence */
491 		edp->state = VT100_EMUL_STATE_NORMAL;
492 		break;
493 	case ASCII_LF:
494 	case ASCII_VT:
495 	case ASCII_FF:
496 		rc = wsemul_vt100_nextline(edp);
497 		break;
498 	}
499 
500 	if (COLS_LEFT != 0)
501 		edp->flags &= ~VTFL_LASTCHAR;
502 
503 	return rc;
504 }
505 
506 int
wsemul_vt100_output_esc(struct wsemul_vt100_emuldata * edp,struct wsemul_inputstate * instate,int kernel)507 wsemul_vt100_output_esc(struct wsemul_vt100_emuldata *edp,
508     struct wsemul_inputstate *instate, int kernel)
509 {
510 	u_int newstate = VT100_EMUL_STATE_NORMAL;
511 	int rc = 0;
512 	int i;
513 
514 	switch (instate->inchar) {
515 	case '[': /* CSI */
516 		edp->nargs = 0;
517 		memset(edp->args, 0, sizeof (edp->args));
518 		edp->modif1 = edp->modif2 = '\0';
519 		newstate = VT100_EMUL_STATE_CSI;
520 		break;
521 	case '7': /* DECSC */
522 		edp->flags |= VTFL_SAVEDCURS;
523 		edp->savedcursor_row = edp->crow;
524 		edp->savedcursor_col = edp->ccol;
525 		edp->savedattr = edp->curattr;
526 		edp->savedbkgdattr = edp->bkgdattr;
527 		edp->savedattrflags = edp->attrflags;
528 		edp->savedfgcol = edp->fgcol;
529 		edp->savedbgcol = edp->bgcol;
530 		for (i = 0; i < 4; i++)
531 			edp->savedchartab_G[i] = edp->chartab_G[i];
532 		edp->savedchartab0 = edp->chartab0;
533 		edp->savedchartab1 = edp->chartab1;
534 		break;
535 	case '8': /* DECRC */
536 		if ((edp->flags & VTFL_SAVEDCURS) == 0)
537 			break;
538 		edp->crow = edp->savedcursor_row;
539 		edp->ccol = edp->savedcursor_col;
540 		edp->curattr = edp->savedattr;
541 		edp->bkgdattr = edp->savedbkgdattr;
542 		edp->attrflags = edp->savedattrflags;
543 		edp->fgcol = edp->savedfgcol;
544 		edp->bgcol = edp->savedbgcol;
545 		for (i = 0; i < 4; i++)
546 			edp->chartab_G[i] = edp->savedchartab_G[i];
547 		edp->chartab0 = edp->savedchartab0;
548 		edp->chartab1 = edp->savedchartab1;
549 		break;
550 	case '=': /* DECKPAM application mode */
551 		edp->flags |= VTFL_APPLKEYPAD;
552 		break;
553 	case '>': /* DECKPNM numeric mode */
554 		edp->flags &= ~VTFL_APPLKEYPAD;
555 		break;
556 	case 'E': /* NEL */
557 		edp->ccol = 0;
558 		/* FALLTHROUGH */
559 	case 'D': /* IND */
560 		rc = wsemul_vt100_nextline(edp);
561 		break;
562 	case 'H': /* HTS */
563 		if (edp->tabs != NULL)
564 			edp->tabs[edp->ccol] = 1;
565 		break;
566 	case '~': /* LS1R */
567 		edp->flags &= ~VTFL_UTF8;
568 		edp->chartab1 = 1;
569 		break;
570 	case 'n': /* LS2 */
571 		edp->flags &= ~VTFL_UTF8;
572 		edp->chartab0 = 2;
573 		break;
574 	case '}': /* LS2R */
575 		edp->flags &= ~VTFL_UTF8;
576 		edp->chartab1 = 2;
577 		break;
578 	case 'o': /* LS3 */
579 		edp->flags &= ~VTFL_UTF8;
580 		edp->chartab0 = 3;
581 		break;
582 	case '|': /* LS3R */
583 		edp->flags &= ~VTFL_UTF8;
584 		edp->chartab1 = 3;
585 		break;
586 	case 'N': /* SS2 */
587 		edp->flags &= ~VTFL_UTF8;
588 		edp->sschartab = 2;
589 		break;
590 	case 'O': /* SS3 */
591 		edp->flags &= ~VTFL_UTF8;
592 		edp->sschartab = 3;
593 		break;
594 	case 'M': /* RI */
595 		i = ROWS_ABOVE;
596 		if (i > 0) {
597 			if (edp->crow > 0)
598 				edp->crow--;
599 			CHECK_DW;
600 		} else if (i == 0) {
601 			/* Top of scroll region. */
602 			rc = wsemul_vt100_scrolldown(edp, 1);
603 		}
604 		break;
605 	case 'P': /* DCS */
606 		edp->nargs = 0;
607 		memset(edp->args, 0, sizeof (edp->args));
608 		newstate = VT100_EMUL_STATE_DCS;
609 		break;
610 	case 'c': /* RIS */
611 		wsemul_vt100_reset(edp);
612 		rc = wsemul_vt100_ed(edp, 2);
613 		if (rc != 0)
614 			break;
615 		edp->ccol = edp->crow = 0;
616 		break;
617 	case '(': case ')': case '*': case '+': /* SCS */
618 		edp->designating = instate->inchar - '(';
619 		newstate = VT100_EMUL_STATE_SCS94;
620 		break;
621 	case '-': case '.': case '/': /* SCS */
622 		edp->designating = instate->inchar - '-' + 1;
623 		newstate = VT100_EMUL_STATE_SCS96;
624 		break;
625 	case '#':
626 		newstate = VT100_EMUL_STATE_ESC_HASH;
627 		break;
628 	case ' ': /* 7/8 bit */
629 		newstate = VT100_EMUL_STATE_ESC_SPC;
630 		break;
631 	case ']': /* OSC operating system command */
632 	case '^': /* PM privacy message */
633 	case '_': /* APC application program command */
634 		/* ignored */
635 		newstate = VT100_EMUL_STATE_STRING;
636 		break;
637 	case '<': /* exit VT52 mode - ignored */
638 		break;
639 	case '%': /* UTF-8 encoding sequences */
640 		newstate = VT100_EMUL_STATE_ESC_PERCENT;
641 		break;
642 	default:
643 #ifdef VT100_PRINTUNKNOWN
644 		printf("ESC %x unknown\n", instate->inchar);
645 #endif
646 		break;
647 	}
648 
649 	if (COLS_LEFT != 0)
650 		edp->flags &= ~VTFL_LASTCHAR;
651 
652 	if (rc != 0)
653 		return rc;
654 
655 	edp->state = newstate;
656 	return 0;
657 }
658 
659 int
wsemul_vt100_output_scs94(struct wsemul_vt100_emuldata * edp,struct wsemul_inputstate * instate,int kernel)660 wsemul_vt100_output_scs94(struct wsemul_vt100_emuldata *edp,
661     struct wsemul_inputstate *instate, int kernel)
662 {
663 	u_int newstate = VT100_EMUL_STATE_NORMAL;
664 
665 	switch (instate->inchar) {
666 	case '%': /* probably DEC supplemental graphic */
667 		newstate = VT100_EMUL_STATE_SCS94_PERCENT;
668 		break;
669 	case 'A': /* british / national */
670 		edp->flags &= ~VTFL_UTF8;
671 		edp->chartab_G[edp->designating] = edp->nrctab;
672 		break;
673 	case 'B': /* ASCII */
674 		edp->flags &= ~VTFL_UTF8;
675 		edp->chartab_G[edp->designating] = 0;
676 		break;
677 	case '<': /* user preferred supplemental */
678 		/* XXX not really "user" preferred */
679 		edp->flags &= ~VTFL_UTF8;
680 		edp->chartab_G[edp->designating] = edp->isolatin1tab;
681 		break;
682 	case '0': /* DEC special graphic */
683 		edp->flags &= ~VTFL_UTF8;
684 		edp->chartab_G[edp->designating] = edp->decgraphtab;
685 		break;
686 	case '>': /* DEC tech */
687 		edp->flags &= ~VTFL_UTF8;
688 		edp->chartab_G[edp->designating] = edp->dectechtab;
689 		break;
690 	default:
691 #ifdef VT100_PRINTUNKNOWN
692 		printf("ESC%c %x unknown\n", edp->designating + '(',
693 		    instate->inchar);
694 #endif
695 		break;
696 	}
697 
698 	edp->state = newstate;
699 	return 0;
700 }
701 
702 int
wsemul_vt100_output_scs94_percent(struct wsemul_vt100_emuldata * edp,struct wsemul_inputstate * instate,int kernel)703 wsemul_vt100_output_scs94_percent(struct wsemul_vt100_emuldata *edp,
704     struct wsemul_inputstate *instate, int kernel)
705 {
706 	switch (instate->inchar) {
707 	case '5': /* DEC supplemental graphic */
708 		/* XXX there are differences */
709 		edp->flags &= ~VTFL_UTF8;
710 		edp->chartab_G[edp->designating] = edp->isolatin1tab;
711 		break;
712 	default:
713 #ifdef VT100_PRINTUNKNOWN
714 		printf("ESC%c%% %x unknown\n", edp->designating + '(',
715 		    instate->inchar);
716 #endif
717 		break;
718 	}
719 
720 	edp->state = VT100_EMUL_STATE_NORMAL;
721 	return 0;
722 }
723 
724 int
wsemul_vt100_output_scs96(struct wsemul_vt100_emuldata * edp,struct wsemul_inputstate * instate,int kernel)725 wsemul_vt100_output_scs96(struct wsemul_vt100_emuldata *edp,
726     struct wsemul_inputstate *instate, int kernel)
727 {
728 	u_int newstate = VT100_EMUL_STATE_NORMAL;
729 	int nrc;
730 
731 	switch (instate->inchar) {
732 	case '%': /* probably portuguese */
733 		newstate = VT100_EMUL_STATE_SCS96_PERCENT;
734 		break;
735 	case 'A': /* ISO-latin-1 supplemental */
736 		edp->flags &= ~VTFL_UTF8;
737 		edp->chartab_G[edp->designating] = edp->isolatin1tab;
738 		break;
739 	case '4': /* dutch */
740 		nrc = 1;
741 		goto setnrc;
742 	case '5': case 'C': /* finnish */
743 		nrc = 2;
744 		goto setnrc;
745 	case 'R': /* french */
746 		nrc = 3;
747 		goto setnrc;
748 	case 'Q': /* french canadian */
749 		nrc = 4;
750 		goto setnrc;
751 	case 'K': /* german */
752 		nrc = 5;
753 		goto setnrc;
754 	case 'Y': /* italian */
755 		nrc = 6;
756 		goto setnrc;
757 	case 'E': case '6': /* norwegian / danish */
758 		nrc = 7;
759 		goto setnrc;
760 	case 'Z': /* spanish */
761 		nrc = 9;
762 		goto setnrc;
763 	case '7': case 'H': /* swedish */
764 		nrc = 10;
765 		goto setnrc;
766 	case '=': /* swiss */
767 		nrc = 11;
768 setnrc:
769 		if (vt100_setnrc(edp, nrc) == 0) /* what table ??? */
770 			break;
771 		/* else FALLTHROUGH */
772 	default:
773 #ifdef VT100_PRINTUNKNOWN
774 		printf("ESC%c %x unknown\n", edp->designating + '-' - 1,
775 		    instate->inchar);
776 #endif
777 		break;
778 	}
779 
780 	edp->state = newstate;
781 	return 0;
782 }
783 
784 int
wsemul_vt100_output_scs96_percent(struct wsemul_vt100_emuldata * edp,struct wsemul_inputstate * instate,int kernel)785 wsemul_vt100_output_scs96_percent(struct wsemul_vt100_emuldata *edp,
786     struct wsemul_inputstate *instate, int kernel)
787 {
788 	switch (instate->inchar) {
789 	case '6': /* portuguese */
790 		if (vt100_setnrc(edp, 8) == 0)
791 			break;
792 		/* else FALLTHROUGH */
793 	default:
794 #ifdef VT100_PRINTUNKNOWN
795 		printf("ESC%c%% %x unknown\n", edp->designating + '-' - 1,
796 		    instate->inchar);
797 #endif
798 		break;
799 	}
800 
801 	edp->state = VT100_EMUL_STATE_NORMAL;
802 	return 0;
803 }
804 
805 int
wsemul_vt100_output_esc_spc(struct wsemul_vt100_emuldata * edp,struct wsemul_inputstate * instate,int kernel)806 wsemul_vt100_output_esc_spc(struct wsemul_vt100_emuldata *edp,
807     struct wsemul_inputstate *instate, int kernel)
808 {
809 	switch (instate->inchar) {
810 	case 'F': /* 7-bit controls */
811 	case 'G': /* 8-bit controls */
812 #ifdef VT100_PRINTNOTIMPL
813 		printf("ESC<SPC> %x ignored\n", instate->inchar);
814 #endif
815 		break;
816 	default:
817 #ifdef VT100_PRINTUNKNOWN
818 		printf("ESC<SPC> %x unknown\n", instate->inchar);
819 #endif
820 		break;
821 	}
822 
823 	edp->state = VT100_EMUL_STATE_NORMAL;
824 	return 0;
825 }
826 
827 int
wsemul_vt100_output_string(struct wsemul_vt100_emuldata * edp,struct wsemul_inputstate * instate,int kernel)828 wsemul_vt100_output_string(struct wsemul_vt100_emuldata *edp,
829     struct wsemul_inputstate *instate, int kernel)
830 {
831 	if (edp->dcsarg && edp->dcstype && edp->dcspos < DCS_MAXLEN) {
832 		if (instate->inchar & ~0xff) {
833 #ifdef VT100_PRINTUNKNOWN
834 			printf("unknown char %x in DCS\n", instate->inchar);
835 #endif
836 		} else
837 			edp->dcsarg[edp->dcspos++] = (char)instate->inchar;
838 	}
839 
840 	edp->state = VT100_EMUL_STATE_STRING;
841 	return 0;
842 }
843 
844 int
wsemul_vt100_output_string_esc(struct wsemul_vt100_emuldata * edp,struct wsemul_inputstate * instate,int kernel)845 wsemul_vt100_output_string_esc(struct wsemul_vt100_emuldata *edp,
846     struct wsemul_inputstate *instate, int kernel)
847 {
848 	if (instate->inchar == '\\') { /* ST complete */
849 		wsemul_vt100_handle_dcs(edp);
850 		edp->state = VT100_EMUL_STATE_NORMAL;
851 	} else
852 		edp->state = VT100_EMUL_STATE_STRING;
853 
854 	return 0;
855 }
856 
857 int
wsemul_vt100_output_dcs(struct wsemul_vt100_emuldata * edp,struct wsemul_inputstate * instate,int kernel)858 wsemul_vt100_output_dcs(struct wsemul_vt100_emuldata *edp,
859     struct wsemul_inputstate *instate, int kernel)
860 {
861 	u_int newstate = VT100_EMUL_STATE_DCS;
862 
863 	switch (instate->inchar) {
864 	case '0': case '1': case '2': case '3': case '4':
865 	case '5': case '6': case '7': case '8': case '9':
866 		/* argument digit */
867 		if (edp->nargs >= VT100_EMUL_NARGS)
868 			break;
869 		edp->args[edp->nargs] = (edp->args[edp->nargs] * 10) +
870 		    (instate->inchar - '0');
871 		break;
872 	case ';': /* argument terminator */
873 		if (edp->nargs < VT100_EMUL_NARGS)
874 			edp->nargs++;
875 		break;
876 	default:
877 		if (edp->nargs < VT100_EMUL_NARGS)
878 			edp->nargs++;
879 		newstate = VT100_EMUL_STATE_STRING;
880 		switch (instate->inchar) {
881 		case '$':
882 			newstate = VT100_EMUL_STATE_DCS_DOLLAR;
883 			break;
884 		case '{': /* DECDLD soft charset */	/* } */
885 		case '!': /* DECRQUPSS user preferred supplemental set */
886 			/* 'u' must follow - need another state */
887 		case '|': /* DECUDK program F6..F20 */
888 #ifdef VT100_PRINTNOTIMPL
889 			printf("DCS%c ignored\n", (char)instate->inchar);
890 #endif
891 			break;
892 		default:
893 #ifdef VT100_PRINTUNKNOWN
894 			printf("DCS %x (%d, %d) unknown\n", instate->inchar,
895 			    ARG(0), ARG(1));
896 #endif
897 			break;
898 		}
899 	}
900 
901 	edp->state = newstate;
902 	return 0;
903 }
904 
905 int
wsemul_vt100_output_dcs_dollar(struct wsemul_vt100_emuldata * edp,struct wsemul_inputstate * instate,int kernel)906 wsemul_vt100_output_dcs_dollar(struct wsemul_vt100_emuldata *edp,
907     struct wsemul_inputstate *instate, int kernel)
908 {
909 	switch (instate->inchar) {
910 	case 'p': /* DECRSTS terminal state restore */
911 	case 'q': /* DECRQSS control function request */
912 #ifdef VT100_PRINTNOTIMPL
913 		printf("DCS$%c ignored\n", (char)instate->inchar);
914 #endif
915 		break;
916 	case 't': /* DECRSPS restore presentation state */
917 		switch (ARG(0)) {
918 		case 0: /* error */
919 			break;
920 		case 1: /* cursor information restore */
921 #ifdef VT100_PRINTNOTIMPL
922 			printf("DCS1$t ignored\n");
923 #endif
924 			break;
925 		case 2: /* tab stop restore */
926 			edp->dcspos = 0;
927 			edp->dcstype = DCSTYPE_TABRESTORE;
928 			break;
929 		default:
930 #ifdef VT100_PRINTUNKNOWN
931 			printf("DCS%d$t unknown\n", ARG(0));
932 #endif
933 			break;
934 		}
935 		break;
936 	default:
937 #ifdef VT100_PRINTUNKNOWN
938 		printf("DCS$ %x (%d, %d) unknown\n",
939 		    instate->inchar, ARG(0), ARG(1));
940 #endif
941 		break;
942 	}
943 
944 	edp->state = VT100_EMUL_STATE_STRING;
945 	return 0;
946 }
947 
948 int
wsemul_vt100_output_esc_percent(struct wsemul_vt100_emuldata * edp,struct wsemul_inputstate * instate,int kernel)949 wsemul_vt100_output_esc_percent(struct wsemul_vt100_emuldata *edp,
950     struct wsemul_inputstate *instate, int kernel)
951 {
952 	switch (instate->inchar) {
953 #ifdef HAVE_UTF8_SUPPORT
954 	case 'G':
955 		edp->flags |= VTFL_UTF8;
956 		edp->kstate.mbleft = edp->instate.mbleft = 0;
957 		break;
958 	case '@':
959 		edp->flags &= ~VTFL_UTF8;
960 		break;
961 #endif
962 	default:
963 #ifdef VT100_PRINTUNKNOWN
964 		printf("ESC% %x unknown\n", instate->inchar);
965 #endif
966 		break;
967 	}
968 	edp->state = VT100_EMUL_STATE_NORMAL;
969 	return 0;
970 }
971 
972 int
wsemul_vt100_output_esc_hash(struct wsemul_vt100_emuldata * edp,struct wsemul_inputstate * instate,int kernel)973 wsemul_vt100_output_esc_hash(struct wsemul_vt100_emuldata *edp,
974     struct wsemul_inputstate *instate, int kernel)
975 {
976 	int rc = 0;
977 
978 	switch (instate->inchar) {
979 	case '5': /*  DECSWL single width, single height */
980 #ifdef HAVE_DOUBLE_WIDTH_HEIGHT
981 		if (edp->dblwid != NULL && edp->dw != 0) {
982 			int i;
983 			for (i = 0; i < edp->ncols / 2; i++) {
984 				WSEMULOP(rc, edp, &edp->abortstate, copycols,
985 				    (edp->emulcookie, edp->crow, 2 * i, i, 1));
986 				if (rc != 0)
987 					return rc;
988 			}
989 			WSEMULOP(rc, edp, &edp->abortstate, erasecols,
990 			    (edp->emulcookie, edp->crow, i, edp->ncols - i,
991 			     edp->bkgdattr));
992 			if (rc != 0)
993 				return rc;
994 			edp->dblwid[edp->crow] = 0;
995 			edp->dw = 0;
996 		}
997 #endif
998 		break;
999 	case '6': /*  DECDWL double width, single height */
1000 	case '3': /*  DECDHL double width, double height, top half */
1001 	case '4': /*  DECDHL double width, double height, bottom half */
1002 #ifdef HAVE_DOUBLE_WIDTH_HEIGHT
1003 		if (edp->dblwid != NULL && edp->dw == 0) {
1004 			int i;
1005 			for (i = edp->ncols / 2 - 1; i >= 0; i--) {
1006 				WSEMULOP(rc, edp, &edp->abortstate, copycols,
1007 				    (edp->emulcookie, edp->crow, i, 2 * i, 1));
1008 				if (rc != 0)
1009 					return rc;
1010 			}
1011 			for (i = 0; i < edp->ncols / 2; i++) {
1012 				WSEMULOP(rc, edp, &edp->abortstate, erasecols,
1013 				    (edp->emulcookie, edp->crow, 2 * i + 1, 1,
1014 				     edp->bkgdattr));
1015 				if (rc != 0)
1016 					return rc;
1017 			}
1018 			edp->dblwid[edp->crow] = 1;
1019 			edp->dw = 1;
1020 			if (edp->ccol > (edp->ncols >> 1) - 1)
1021 				edp->ccol = (edp->ncols >> 1) - 1;
1022 		}
1023 #endif
1024 		break;
1025 	case '8': { /* DECALN */
1026 		int i, j;
1027 		for (i = 0; i < edp->nrows; i++)
1028 			for (j = 0; j < edp->ncols; j++) {
1029 				WSEMULOP(rc, edp, &edp->abortstate, putchar,
1030 				    (edp->emulcookie, i, j, 'E', edp->curattr));
1031 				if (rc != 0)
1032 					return rc;
1033 			}
1034 		}
1035 		edp->ccol = 0;
1036 		edp->crow = 0;
1037 		break;
1038 	default:
1039 #ifdef VT100_PRINTUNKNOWN
1040 		printf("ESC# %x unknown\n", instate->inchar);
1041 #endif
1042 		break;
1043 	}
1044 
1045 	if (COLS_LEFT != 0)
1046 		edp->flags &= ~VTFL_LASTCHAR;
1047 
1048 	edp->state = VT100_EMUL_STATE_NORMAL;
1049 	return 0;
1050 }
1051 
1052 int
wsemul_vt100_output_csi(struct wsemul_vt100_emuldata * edp,struct wsemul_inputstate * instate,int kernel)1053 wsemul_vt100_output_csi(struct wsemul_vt100_emuldata *edp,
1054     struct wsemul_inputstate *instate, int kernel)
1055 {
1056 	u_int newstate = VT100_EMUL_STATE_CSI;
1057 	int oargs;
1058 	int rc = 0;
1059 
1060 	switch (instate->inchar) {
1061 	case '0': case '1': case '2': case '3': case '4':
1062 	case '5': case '6': case '7': case '8': case '9':
1063 		/* argument digit */
1064 		if (edp->nargs > VT100_EMUL_NARGS - 1)
1065 			break;
1066 		edp->args[edp->nargs] = (edp->args[edp->nargs] * 10) +
1067 		    (instate->inchar - '0');
1068 		break;
1069 	case ';': /* argument terminator */
1070 		if (edp->nargs < VT100_EMUL_NARGS)
1071 			edp->nargs++;
1072 		break;
1073 	case '?': /* DEC specific */
1074 	case '>': /* DA query */
1075 		edp->modif1 = (char)instate->inchar;
1076 		break;
1077 	case '!':
1078 	case '"':
1079 	case '$':
1080 	case '&':
1081 		edp->modif2 = (char)instate->inchar;
1082 		break;
1083 	default: /* end of escape sequence */
1084 		oargs = edp->nargs;
1085 		if (edp->nargs < VT100_EMUL_NARGS)
1086 			edp->nargs++;
1087 		rc = wsemul_vt100_handle_csi(edp, instate, kernel);
1088 		if (rc != 0) {
1089 			/* undo nargs progress */
1090 			edp->nargs = oargs;
1091 			return rc;
1092 		}
1093 		newstate = VT100_EMUL_STATE_NORMAL;
1094 		break;
1095 	}
1096 
1097 	if (COLS_LEFT != 0)
1098 		edp->flags &= ~VTFL_LASTCHAR;
1099 
1100 	edp->state = newstate;
1101 	return 0;
1102 }
1103 
1104 u_int
wsemul_vt100_output(void * cookie,const u_char * data,u_int count,int kernel)1105 wsemul_vt100_output(void *cookie, const u_char *data, u_int count, int kernel)
1106 {
1107 	struct wsemul_vt100_emuldata *edp = cookie;
1108 	struct wsemul_inputstate *instate;
1109 	u_int prev_count, processed = 0;
1110 #ifdef HAVE_JUMP_SCROLL
1111 	int lines;
1112 #endif
1113 	int rc = 0;
1114 
1115 #ifdef DIAGNOSTIC
1116 	if (kernel && !edp->console)
1117 		panic("wsemul_vt100_output: kernel output, not console");
1118 #endif
1119 
1120 	instate = kernel ? &edp->kstate : &edp->instate;
1121 
1122 	switch (edp->abortstate.state) {
1123 	case ABORT_FAILED_CURSOR:
1124 		/*
1125 		 * If we could not display the cursor back, we pretended not
1126 		 * having been able to process the last byte. But this
1127 		 * is a lie, so compensate here.
1128 		 */
1129 		data++, count--;
1130 		processed++;
1131 		wsemul_reset_abortstate(&edp->abortstate);
1132 		break;
1133 	case ABORT_OK:
1134 		/* remove cursor image if visible */
1135 		if (edp->flags & VTFL_CURSORON) {
1136 			rc = (*edp->emulops->cursor)
1137 			    (edp->emulcookie, 0, edp->crow,
1138 #ifdef HAVE_DOUBLE_WIDTH_HEIGHT
1139 			     edp->ccol << edp->dw);
1140 #else
1141 			     edp->ccol);
1142 #endif
1143 			if (rc != 0)
1144 				return 0;
1145 		}
1146 		break;
1147 	default:
1148 		break;
1149 	}
1150 
1151 	for (;;) {
1152 #ifdef HAVE_JUMP_SCROLL
1153 		switch (edp->abortstate.state) {
1154 		case ABORT_FAILED_JUMP_SCROLL:
1155 			/*
1156 			 * If we failed a previous jump scroll attempt, we
1157 			 * need to try to resume it with the same distance.
1158 			 * We can not recompute it since there might be more
1159 			 * bytes in the tty ring, causing a different result.
1160 			 */
1161 			lines = edp->abortstate.lines;
1162 			break;
1163 		case ABORT_OK:
1164 			/*
1165 			 * If we are at the bottom of the scrolling area, count
1166 			 * newlines until an escape sequence appears.
1167 			 */
1168 			if ((edp->state == VT100_EMUL_STATE_NORMAL || kernel) &&
1169 			    ROWS_BELOW == 0)
1170 				lines = wsemul_vt100_jump_scroll(edp, data,
1171 				    count, kernel);
1172 			else
1173 				lines = 0;
1174 			break;
1175 		default:
1176 			/*
1177 			 * If we are recovering a non-scrolling failure,
1178 			 * do not try to scroll yet.
1179 			 */
1180 			lines = 0;
1181 			break;
1182 		}
1183 
1184 		if (lines > 1) {
1185 			wsemul_resume_abort(&edp->abortstate);
1186 			rc = wsemul_vt100_scrollup(edp, lines);
1187 			if (rc != 0) {
1188 				wsemul_abort_jump_scroll(&edp->abortstate,
1189 				    lines);
1190 				return processed;
1191 			}
1192 			wsemul_reset_abortstate(&edp->abortstate);
1193 			edp->crow -= lines;
1194 		}
1195 #endif
1196 
1197 		wsemul_resume_abort(&edp->abortstate);
1198 
1199 		prev_count = count;
1200 		if (wsemul_getchar(&data, &count, instate,
1201 #ifdef HAVE_UTF8_SUPPORT
1202 		    (edp->state == VT100_EMUL_STATE_NORMAL && !kernel) ?
1203 		      edp->flags & VTFL_UTF8 : 0
1204 #else
1205 		    0
1206 #endif
1207 		    ) != 0)
1208 			break;
1209 
1210 		if (!(instate->inchar & ~0xff) &&
1211 		    (instate->inchar & 0x7f) < 0x20) {
1212 			rc = wsemul_vt100_output_c0c1(edp, instate, kernel);
1213 			if (rc != 0)
1214 				break;
1215 			processed += prev_count - count;
1216  			continue;
1217  		}
1218 
1219 		if (edp->state == VT100_EMUL_STATE_NORMAL || kernel) {
1220 			rc =
1221 			    wsemul_vt100_output_normal(edp, instate, kernel, 1);
1222 			if (rc != 0)
1223 				break;
1224 			instate->last_output = instate->inchar;
1225 			processed += prev_count - count;
1226 			continue;
1227 		}
1228 #ifdef DIAGNOSTIC
1229 		if (edp->state > nitems(vt100_output))
1230 			panic("wsemul_vt100: invalid state %d", edp->state);
1231 #endif
1232 		rc = vt100_output[edp->state - 1](edp, instate, kernel);
1233 		if (rc != 0)
1234 			break;
1235 		processed += prev_count - count;
1236 	}
1237 
1238 	if (rc != 0)
1239 		wsemul_abort_other(&edp->abortstate);
1240 	else {
1241 		/* put cursor image back if visible */
1242 		if (edp->flags & VTFL_CURSORON) {
1243 			rc = (*edp->emulops->cursor)
1244 			    (edp->emulcookie, 1, edp->crow,
1245 #ifdef HAVE_DOUBLE_WIDTH_HEIGHT
1246 			     edp->ccol << edp->dw);
1247 #else
1248 			     edp->ccol);
1249 #endif
1250 			if (rc != 0) {
1251 				/*
1252 				 * Pretend the last byte hasn't been processed,
1253 				 * while remembering that only the cursor
1254 				 * operation really needs to be done.
1255 				 */
1256 				wsemul_abort_cursor(&edp->abortstate);
1257 				processed--;
1258 			}
1259 		}
1260 	}
1261 
1262 	if (rc == 0)
1263 		wsemul_reset_abortstate(&edp->abortstate);
1264 
1265 	return processed;
1266 }
1267 
1268 #ifdef HAVE_JUMP_SCROLL
1269 int
wsemul_vt100_jump_scroll(struct wsemul_vt100_emuldata * edp,const u_char * data,u_int count,int kernel)1270 wsemul_vt100_jump_scroll(struct wsemul_vt100_emuldata *edp, const u_char *data,
1271     u_int count, int kernel)
1272 {
1273 	struct wsemul_inputstate tmpstate;
1274 	u_int pos, lines;
1275 
1276 	lines = 0;
1277 	pos = edp->ccol;
1278 	tmpstate = kernel ? edp->kstate : edp->instate;	/* structure copy */
1279 
1280 	while (wsemul_getchar(&data, &count, &tmpstate,
1281 #ifdef HAVE_UTF8_SUPPORT
1282 	    kernel ? 0 : edp->flags & VTFL_UTF8
1283 #else
1284 	    0
1285 #endif
1286 	    ) == 0) {
1287 		/*
1288 		 * Only char causing a transition from
1289 		 * VT100_EMUL_STATE_NORMAL to another state, for now.
1290 		 * Revisit this if this changes...
1291 		 */
1292 		if (tmpstate.inchar == ASCII_ESC)
1293 			break;
1294 
1295 		if (ISSET(edp->flags, VTFL_DECAWM))
1296 			switch (tmpstate.inchar) {
1297 			case ASCII_BS:
1298 				if (pos > 0)
1299 					pos--;
1300 				break;
1301 			case ASCII_CR:
1302 				pos = 0;
1303 				break;
1304 			case ASCII_HT:
1305 				if (edp->tabs) {
1306 					pos++;
1307 					while (pos < NCOLS - 1 &&
1308 					    edp->tabs[pos] == 0)
1309 						pos++;
1310 				} else {
1311 					pos = (pos + 7) & ~7;
1312 					if (pos >= NCOLS)
1313 						pos = NCOLS - 1;
1314 				}
1315 				break;
1316 			default:
1317 				if (!(tmpstate.inchar & ~0xff) &&
1318 				    (tmpstate.inchar & 0x7f) < 0x20)
1319 					break;
1320 				if (pos++ >= NCOLS) {
1321 					pos = 0;
1322 					tmpstate.inchar = ASCII_LF;
1323 				}
1324 				break;
1325 			}
1326 
1327 		if (tmpstate.inchar == ASCII_LF ||
1328 		    tmpstate.inchar == ASCII_VT ||
1329 		    tmpstate.inchar == ASCII_FF) {
1330 			if (++lines >= edp->scrreg_nrows - 1)
1331 				break;
1332 		}
1333 	}
1334 
1335 	return lines;
1336 }
1337 #endif
1338