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