xref: /openbsd/sys/dev/wscons/wsemul_vt100.c (revision 91f110e0)
1 /* $OpenBSD: wsemul_vt100.c,v 1.31 2013/10/18 22:06:41 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, long);
61 void	*wsemul_vt100_attach(int, const struct wsscreen_descr *,
62 				  void *, int, int, void *, long);
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, long);
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     long 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, long 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->alloc_attr)(cookie,
181 		    WS_KERNEL_FG, WS_KERNEL_BG,
182 		    WS_KERNEL_COLATTR | WSATTR_WSCOLORS, &edp->kernattr);
183 	else
184 		res = (*edp->emulops->alloc_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, long 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); 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);
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 #if 0
476 	case CSI: /* 8-bit */
477 		/* XXX cancel current escape sequence */
478 		edp->nargs = 0;
479 		memset(edp->args, 0, sizeof (edp->args));
480 		edp->modif1 = edp->modif2 = '\0';
481 		edp->state = VT100_EMUL_STATE_CSI;
482 		break;
483 	case DCS: /* 8-bit */
484 		/* XXX cancel current escape sequence */
485 		edp->nargs = 0;
486 		memset(edp->args, 0, sizeof (edp->args));
487 		edp->state = VT100_EMUL_STATE_DCS;
488 		break;
489 	case ST: /* string end 8-bit */
490 		/* XXX only in VT100_EMUL_STATE_STRING */
491 		wsemul_vt100_handle_dcs(edp);
492 		return (VT100_EMUL_STATE_NORMAL);
493 #endif
494 	case ASCII_LF:
495 	case ASCII_VT:
496 	case ASCII_FF:
497 		rc = wsemul_vt100_nextline(edp);
498 		break;
499 	}
500 
501 	if (COLS_LEFT != 0)
502 		edp->flags &= ~VTFL_LASTCHAR;
503 
504 	return rc;
505 }
506 
507 int
508 wsemul_vt100_output_esc(struct wsemul_vt100_emuldata *edp,
509     struct wsemul_inputstate *instate)
510 {
511 	u_int newstate = VT100_EMUL_STATE_NORMAL;
512 	int rc = 0;
513 	int i;
514 
515 	switch (instate->inchar) {
516 	case '[': /* CSI */
517 		edp->nargs = 0;
518 		memset(edp->args, 0, sizeof (edp->args));
519 		edp->modif1 = edp->modif2 = '\0';
520 		newstate = VT100_EMUL_STATE_CSI;
521 		break;
522 	case '7': /* DECSC */
523 		edp->flags |= VTFL_SAVEDCURS;
524 		edp->savedcursor_row = edp->crow;
525 		edp->savedcursor_col = edp->ccol;
526 		edp->savedattr = edp->curattr;
527 		edp->savedbkgdattr = edp->bkgdattr;
528 		edp->savedattrflags = edp->attrflags;
529 		edp->savedfgcol = edp->fgcol;
530 		edp->savedbgcol = edp->bgcol;
531 		for (i = 0; i < 4; i++)
532 			edp->savedchartab_G[i] = edp->chartab_G[i];
533 		edp->savedchartab0 = edp->chartab0;
534 		edp->savedchartab1 = edp->chartab1;
535 		break;
536 	case '8': /* DECRC */
537 		if ((edp->flags & VTFL_SAVEDCURS) == 0)
538 			break;
539 		edp->crow = edp->savedcursor_row;
540 		edp->ccol = edp->savedcursor_col;
541 		edp->curattr = edp->savedattr;
542 		edp->bkgdattr = edp->savedbkgdattr;
543 		edp->attrflags = edp->savedattrflags;
544 		edp->fgcol = edp->savedfgcol;
545 		edp->bgcol = edp->savedbgcol;
546 		for (i = 0; i < 4; i++)
547 			edp->chartab_G[i] = edp->savedchartab_G[i];
548 		edp->chartab0 = edp->savedchartab0;
549 		edp->chartab1 = edp->savedchartab1;
550 		break;
551 	case '=': /* DECKPAM application mode */
552 		edp->flags |= VTFL_APPLKEYPAD;
553 		break;
554 	case '>': /* DECKPNM numeric mode */
555 		edp->flags &= ~VTFL_APPLKEYPAD;
556 		break;
557 	case 'E': /* NEL */
558 		edp->ccol = 0;
559 		/* FALLTHROUGH */
560 	case 'D': /* IND */
561 		rc = wsemul_vt100_nextline(edp);
562 		break;
563 	case 'H': /* HTS */
564 		if (edp->tabs != NULL)
565 			edp->tabs[edp->ccol] = 1;
566 		break;
567 	case '~': /* LS1R */
568 		edp->flags &= ~VTFL_UTF8;
569 		edp->chartab1 = 1;
570 		break;
571 	case 'n': /* LS2 */
572 		edp->flags &= ~VTFL_UTF8;
573 		edp->chartab0 = 2;
574 		break;
575 	case '}': /* LS2R */
576 		edp->flags &= ~VTFL_UTF8;
577 		edp->chartab1 = 2;
578 		break;
579 	case 'o': /* LS3 */
580 		edp->flags &= ~VTFL_UTF8;
581 		edp->chartab0 = 3;
582 		break;
583 	case '|': /* LS3R */
584 		edp->flags &= ~VTFL_UTF8;
585 		edp->chartab1 = 3;
586 		break;
587 	case 'N': /* SS2 */
588 		edp->flags &= ~VTFL_UTF8;
589 		edp->sschartab = 2;
590 		break;
591 	case 'O': /* SS3 */
592 		edp->flags &= ~VTFL_UTF8;
593 		edp->sschartab = 3;
594 		break;
595 	case 'M': /* RI */
596 		if (ROWS_ABOVE > 0) {
597 			edp->crow--;
598 			CHECK_DW;
599 			break;
600 		}
601 		rc = wsemul_vt100_scrolldown(edp, 1);
602 		break;
603 	case 'P': /* DCS */
604 		edp->nargs = 0;
605 		memset(edp->args, 0, sizeof (edp->args));
606 		newstate = VT100_EMUL_STATE_DCS;
607 		break;
608 	case 'c': /* RIS */
609 		wsemul_vt100_reset(edp);
610 		rc = wsemul_vt100_ed(edp, 2);
611 		if (rc != 0)
612 			break;
613 		edp->ccol = edp->crow = 0;
614 		break;
615 	case '(': case ')': case '*': case '+': /* SCS */
616 		edp->designating = instate->inchar - '(';
617 		newstate = VT100_EMUL_STATE_SCS94;
618 		break;
619 	case '-': case '.': case '/': /* SCS */
620 		edp->designating = instate->inchar - '-' + 1;
621 		newstate = VT100_EMUL_STATE_SCS96;
622 		break;
623 	case '#':
624 		newstate = VT100_EMUL_STATE_ESC_HASH;
625 		break;
626 	case ' ': /* 7/8 bit */
627 		newstate = VT100_EMUL_STATE_ESC_SPC;
628 		break;
629 	case ']': /* OSC operating system command */
630 	case '^': /* PM privacy message */
631 	case '_': /* APC application program command */
632 		/* ignored */
633 		newstate = VT100_EMUL_STATE_STRING;
634 		break;
635 	case '<': /* exit VT52 mode - ignored */
636 		break;
637 	case '%': /* UTF-8 encoding sequences */
638 		newstate = VT100_EMUL_STATE_ESC_PERCENT;
639 		break;
640 	default:
641 #ifdef VT100_PRINTUNKNOWN
642 		printf("ESC %x unknown\n", instate->inchar);
643 #endif
644 		break;
645 	}
646 
647 	if (COLS_LEFT != 0)
648 		edp->flags &= ~VTFL_LASTCHAR;
649 
650 	if (rc != 0)
651 		return rc;
652 
653 	edp->state = newstate;
654 	return 0;
655 }
656 
657 int
658 wsemul_vt100_output_scs94(struct wsemul_vt100_emuldata *edp,
659     struct wsemul_inputstate *instate)
660 {
661 	u_int newstate = VT100_EMUL_STATE_NORMAL;
662 
663 	switch (instate->inchar) {
664 	case '%': /* probably DEC supplemental graphic */
665 		newstate = VT100_EMUL_STATE_SCS94_PERCENT;
666 		break;
667 	case 'A': /* british / national */
668 		edp->flags &= ~VTFL_UTF8;
669 		edp->chartab_G[edp->designating] = edp->nrctab;
670 		break;
671 	case 'B': /* ASCII */
672 		edp->flags &= ~VTFL_UTF8;
673 		edp->chartab_G[edp->designating] = 0;
674 		break;
675 	case '<': /* user preferred supplemental */
676 		/* XXX not really "user" preferred */
677 		edp->flags &= ~VTFL_UTF8;
678 		edp->chartab_G[edp->designating] = edp->isolatin1tab;
679 		break;
680 	case '0': /* DEC special graphic */
681 		edp->flags &= ~VTFL_UTF8;
682 		edp->chartab_G[edp->designating] = edp->decgraphtab;
683 		break;
684 	case '>': /* DEC tech */
685 		edp->flags &= ~VTFL_UTF8;
686 		edp->chartab_G[edp->designating] = edp->dectechtab;
687 		break;
688 	default:
689 #ifdef VT100_PRINTUNKNOWN
690 		printf("ESC%c %x unknown\n", edp->designating + '(',
691 		    instate->inchar);
692 #endif
693 		break;
694 	}
695 
696 	edp->state = newstate;
697 	return 0;
698 }
699 
700 int
701 wsemul_vt100_output_scs94_percent(struct wsemul_vt100_emuldata *edp,
702     struct wsemul_inputstate *instate)
703 {
704 	switch (instate->inchar) {
705 	case '5': /* DEC supplemental graphic */
706 		/* XXX there are differences */
707 		edp->flags &= ~VTFL_UTF8;
708 		edp->chartab_G[edp->designating] = edp->isolatin1tab;
709 		break;
710 	default:
711 #ifdef VT100_PRINTUNKNOWN
712 		printf("ESC%c%% %x unknown\n", edp->designating + '(',
713 		    instate->inchar);
714 #endif
715 		break;
716 	}
717 
718 	edp->state = VT100_EMUL_STATE_NORMAL;
719 	return 0;
720 }
721 
722 int
723 wsemul_vt100_output_scs96(struct wsemul_vt100_emuldata *edp,
724     struct wsemul_inputstate *instate)
725 {
726 	u_int newstate = VT100_EMUL_STATE_NORMAL;
727 	int nrc;
728 
729 	switch (instate->inchar) {
730 	case '%': /* probably portuguese */
731 		newstate = VT100_EMUL_STATE_SCS96_PERCENT;
732 		break;
733 	case 'A': /* ISO-latin-1 supplemental */
734 		edp->flags &= ~VTFL_UTF8;
735 		edp->chartab_G[edp->designating] = edp->isolatin1tab;
736 		break;
737 	case '4': /* dutch */
738 		nrc = 1;
739 		goto setnrc;
740 	case '5': case 'C': /* finnish */
741 		nrc = 2;
742 		goto setnrc;
743 	case 'R': /* french */
744 		nrc = 3;
745 		goto setnrc;
746 	case 'Q': /* french canadian */
747 		nrc = 4;
748 		goto setnrc;
749 	case 'K': /* german */
750 		nrc = 5;
751 		goto setnrc;
752 	case 'Y': /* italian */
753 		nrc = 6;
754 		goto setnrc;
755 	case 'E': case '6': /* norwegian / danish */
756 		nrc = 7;
757 		goto setnrc;
758 	case 'Z': /* spanish */
759 		nrc = 9;
760 		goto setnrc;
761 	case '7': case 'H': /* swedish */
762 		nrc = 10;
763 		goto setnrc;
764 	case '=': /* swiss */
765 		nrc = 11;
766 setnrc:
767 		if (vt100_setnrc(edp, nrc) == 0) /* what table ??? */
768 			break;
769 		/* else FALLTHROUGH */
770 	default:
771 #ifdef VT100_PRINTUNKNOWN
772 		printf("ESC%c %x unknown\n", edp->designating + '-' - 1,
773 		    instate->inchar);
774 #endif
775 		break;
776 	}
777 
778 	edp->state = newstate;
779 	return 0;
780 }
781 
782 int
783 wsemul_vt100_output_scs96_percent(struct wsemul_vt100_emuldata *edp,
784     struct wsemul_inputstate *instate)
785 {
786 	switch (instate->inchar) {
787 	case '6': /* portuguese */
788 		if (vt100_setnrc(edp, 8) == 0)
789 			break;
790 		/* else FALLTHROUGH */
791 	default:
792 #ifdef VT100_PRINTUNKNOWN
793 		printf("ESC%c%% %x unknown\n", edp->designating + '-' - 1,
794 		    instate->inchar);
795 #endif
796 		break;
797 	}
798 
799 	edp->state = VT100_EMUL_STATE_NORMAL;
800 	return 0;
801 }
802 
803 int
804 wsemul_vt100_output_esc_spc(struct wsemul_vt100_emuldata *edp,
805     struct wsemul_inputstate *instate)
806 {
807 	switch (instate->inchar) {
808 	case 'F': /* 7-bit controls */
809 	case 'G': /* 8-bit controls */
810 #ifdef VT100_PRINTNOTIMPL
811 		printf("ESC<SPC> %x ignored\n", instate->inchar);
812 #endif
813 		break;
814 	default:
815 #ifdef VT100_PRINTUNKNOWN
816 		printf("ESC<SPC> %x unknown\n", instate->inchar);
817 #endif
818 		break;
819 	}
820 
821 	edp->state = VT100_EMUL_STATE_NORMAL;
822 	return 0;
823 }
824 
825 int
826 wsemul_vt100_output_string(struct wsemul_vt100_emuldata *edp,
827     struct wsemul_inputstate *instate)
828 {
829 	if (edp->dcstype && edp->dcspos < DCS_MAXLEN) {
830 		if (instate->inchar & ~0xff) {
831 #ifdef VT100_PRINTUNKNOWN
832 			printf("unknown char %x in DCS\n", instate->inchar);
833 #endif
834 		} else
835 			edp->dcsarg[edp->dcspos++] = (char)instate->inchar;
836 	}
837 
838 	edp->state = VT100_EMUL_STATE_STRING;
839 	return 0;
840 }
841 
842 int
843 wsemul_vt100_output_string_esc(struct wsemul_vt100_emuldata *edp,
844     struct wsemul_inputstate *instate)
845 {
846 	if (instate->inchar == '\\') { /* ST complete */
847 		wsemul_vt100_handle_dcs(edp);
848 		edp->state = VT100_EMUL_STATE_NORMAL;
849 	} else
850 		edp->state = VT100_EMUL_STATE_STRING;
851 
852 	return 0;
853 }
854 
855 int
856 wsemul_vt100_output_dcs(struct wsemul_vt100_emuldata *edp,
857     struct wsemul_inputstate *instate)
858 {
859 	u_int newstate = VT100_EMUL_STATE_DCS;
860 
861 	switch (instate->inchar) {
862 	case '0': case '1': case '2': case '3': case '4':
863 	case '5': case '6': case '7': case '8': case '9':
864 		/* argument digit */
865 		if (edp->nargs > VT100_EMUL_NARGS - 1)
866 			break;
867 		edp->args[edp->nargs] = (edp->args[edp->nargs] * 10) +
868 		    (instate->inchar - '0');
869 		break;
870 	case ';': /* argument terminator */
871 		edp->nargs++;
872 		break;
873 	default:
874 		edp->nargs++;
875 		if (edp->nargs > VT100_EMUL_NARGS) {
876 #ifdef VT100_DEBUG
877 			printf("vt100: too many arguments\n");
878 #endif
879 			edp->nargs = VT100_EMUL_NARGS;
880 		}
881 		newstate = VT100_EMUL_STATE_STRING;
882 		switch (instate->inchar) {
883 		case '$':
884 			newstate = VT100_EMUL_STATE_DCS_DOLLAR;
885 			break;
886 		case '{': /* DECDLD soft charset */	/* } */
887 		case '!': /* DECRQUPSS user preferred supplemental set */
888 			/* 'u' must follow - need another state */
889 		case '|': /* DECUDK program F6..F20 */
890 #ifdef VT100_PRINTNOTIMPL
891 			printf("DCS%c ignored\n", (char)instate->inchar);
892 #endif
893 			break;
894 		default:
895 #ifdef VT100_PRINTUNKNOWN
896 			printf("DCS %x (%d, %d) unknown\n", instate->inchar,
897 			    ARG(0), ARG(1));
898 #endif
899 			break;
900 		}
901 	}
902 
903 	edp->state = newstate;
904 	return 0;
905 }
906 
907 int
908 wsemul_vt100_output_dcs_dollar(struct wsemul_vt100_emuldata *edp,
909     struct wsemul_inputstate *instate)
910 {
911 	switch (instate->inchar) {
912 	case 'p': /* DECRSTS terminal state restore */
913 	case 'q': /* DECRQSS control function request */
914 #ifdef VT100_PRINTNOTIMPL
915 		printf("DCS$%c ignored\n", (char)instate->inchar);
916 #endif
917 		break;
918 	case 't': /* DECRSPS restore presentation state */
919 		switch (ARG(0)) {
920 		case 0: /* error */
921 			break;
922 		case 1: /* cursor information restore */
923 #ifdef VT100_PRINTNOTIMPL
924 			printf("DCS1$t ignored\n");
925 #endif
926 			break;
927 		case 2: /* tab stop restore */
928 			edp->dcspos = 0;
929 			edp->dcstype = DCSTYPE_TABRESTORE;
930 			break;
931 		default:
932 #ifdef VT100_PRINTUNKNOWN
933 			printf("DCS%d$t unknown\n", ARG(0));
934 #endif
935 			break;
936 		}
937 		break;
938 	default:
939 #ifdef VT100_PRINTUNKNOWN
940 		printf("DCS$ %x (%d, %d) unknown\n",
941 		    instate->inchar, ARG(0), ARG(1));
942 #endif
943 		break;
944 	}
945 
946 	edp->state = VT100_EMUL_STATE_STRING;
947 	return 0;
948 }
949 
950 int
951 wsemul_vt100_output_esc_percent(struct wsemul_vt100_emuldata *edp,
952     struct wsemul_inputstate *instate)
953 {
954 	switch (instate->inchar) {
955 #ifdef HAVE_UTF8_SUPPORT
956 	case 'G':
957 		edp->flags |= VTFL_UTF8;
958 		edp->kstate.mbleft = edp->instate.mbleft = 0;
959 		break;
960 	case '@':
961 		edp->flags &= ~VTFL_UTF8;
962 		break;
963 #endif
964 	default:
965 #ifdef VT100_PRINTUNKNOWN
966 		printf("ESC% %x unknown\n", instate->inchar);
967 #endif
968 		break;
969 	}
970 	edp->state = VT100_EMUL_STATE_NORMAL;
971 	return 0;
972 }
973 
974 int
975 wsemul_vt100_output_esc_hash(struct wsemul_vt100_emuldata *edp,
976     struct wsemul_inputstate *instate)
977 {
978 	int i;
979 	int rc = 0;
980 
981 	switch (instate->inchar) {
982 	case '5': /*  DECSWL single width, single height */
983 		if (edp->dblwid != NULL && edp->dw != 0) {
984 			for (i = 0; i < edp->ncols / 2; i++) {
985 				WSEMULOP(rc, edp, &edp->abortstate, copycols,
986 				    (edp->emulcookie, edp->crow, 2 * i, i, 1));
987 				if (rc != 0)
988 					return rc;
989 			}
990 			WSEMULOP(rc, edp, &edp->abortstate, erasecols,
991 			    (edp->emulcookie, edp->crow, i, edp->ncols - i,
992 			     edp->bkgdattr));
993 			if (rc != 0)
994 				return rc;
995 			edp->dblwid[edp->crow] = 0;
996 			edp->dw = 0;
997 		}
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 		if (edp->dblwid != NULL && edp->dw == 0) {
1003 			for (i = edp->ncols / 2 - 1; i >= 0; i--) {
1004 				WSEMULOP(rc, edp, &edp->abortstate, copycols,
1005 				    (edp->emulcookie, edp->crow, i, 2 * i, 1));
1006 				if (rc != 0)
1007 					return rc;
1008 			}
1009 			for (i = 0; i < edp->ncols / 2; i++) {
1010 				WSEMULOP(rc, edp, &edp->abortstate, erasecols,
1011 				    (edp->emulcookie, edp->crow, 2 * i + 1, 1,
1012 				     edp->bkgdattr));
1013 				if (rc != 0)
1014 					return rc;
1015 			}
1016 			edp->dblwid[edp->crow] = 1;
1017 			edp->dw = 1;
1018 			if (edp->ccol > (edp->ncols >> 1) - 1)
1019 				edp->ccol = (edp->ncols >> 1) - 1;
1020 		}
1021 		break;
1022 	case '8': { /* DECALN */
1023 		int i, j;
1024 		for (i = 0; i < edp->nrows; i++)
1025 			for (j = 0; j < edp->ncols; j++) {
1026 				WSEMULOP(rc, edp, &edp->abortstate, putchar,
1027 				    (edp->emulcookie, i, j, 'E', edp->curattr));
1028 				if (rc != 0)
1029 					return rc;
1030 			}
1031 		}
1032 		edp->ccol = 0;
1033 		edp->crow = 0;
1034 		break;
1035 	default:
1036 #ifdef VT100_PRINTUNKNOWN
1037 		printf("ESC# %x unknown\n", instate->inchar);
1038 #endif
1039 		break;
1040 	}
1041 
1042 	if (COLS_LEFT != 0)
1043 		edp->flags &= ~VTFL_LASTCHAR;
1044 
1045 	edp->state = VT100_EMUL_STATE_NORMAL;
1046 	return 0;
1047 }
1048 
1049 int
1050 wsemul_vt100_output_csi(struct wsemul_vt100_emuldata *edp,
1051     struct wsemul_inputstate *instate)
1052 {
1053 	u_int newstate = VT100_EMUL_STATE_CSI;
1054 	int oargs;
1055 	int rc = 0;
1056 
1057 	switch (instate->inchar) {
1058 	case '0': case '1': case '2': case '3': case '4':
1059 	case '5': case '6': case '7': case '8': case '9':
1060 		/* argument digit */
1061 		if (edp->nargs > VT100_EMUL_NARGS - 1)
1062 			break;
1063 		edp->args[edp->nargs] = (edp->args[edp->nargs] * 10) +
1064 		    (instate->inchar - '0');
1065 		break;
1066 	case ';': /* argument terminator */
1067 		edp->nargs++;
1068 		break;
1069 	case '?': /* DEC specific */
1070 	case '>': /* DA query */
1071 		edp->modif1 = (char)instate->inchar;
1072 		break;
1073 	case '!':
1074 	case '"':
1075 	case '$':
1076 	case '&':
1077 		edp->modif2 = (char)instate->inchar;
1078 		break;
1079 	default: /* end of escape sequence */
1080 		oargs = edp->nargs++;
1081 		if (edp->nargs > VT100_EMUL_NARGS) {
1082 #ifdef VT100_DEBUG
1083 			printf("vt100: too many arguments\n");
1084 #endif
1085 			edp->nargs = VT100_EMUL_NARGS;
1086 		}
1087 		rc = wsemul_vt100_handle_csi(edp, instate);
1088 		if (rc != 0) {
1089 			edp->nargs = oargs;
1090 			return rc;
1091 		}
1092 		newstate = VT100_EMUL_STATE_NORMAL;
1093 		break;
1094 	}
1095 
1096 	if (COLS_LEFT != 0)
1097 		edp->flags &= ~VTFL_LASTCHAR;
1098 
1099 	edp->state = newstate;
1100 	return 0;
1101 }
1102 
1103 u_int
1104 wsemul_vt100_output(void *cookie, const u_char *data, u_int count, int kernel)
1105 {
1106 	struct wsemul_vt100_emuldata *edp = cookie;
1107 	struct wsemul_inputstate *instate;
1108 	u_int processed = 0;
1109 #ifdef HAVE_JUMP_SCROLL
1110 	int lines;
1111 #endif
1112 	int rc = 0;
1113 
1114 #ifdef DIAGNOSTIC
1115 	if (kernel && !edp->console)
1116 		panic("wsemul_vt100_output: kernel output, not console");
1117 #endif
1118 
1119 	instate = kernel ? &edp->kstate : &edp->instate;
1120 
1121 	switch (edp->abortstate.state) {
1122 	case ABORT_FAILED_CURSOR:
1123 		/*
1124 		 * If we could not display the cursor back, we pretended not
1125 		 * having been able to display the last character. But this
1126 		 * is a lie, so compensate here.
1127 		 */
1128 		data++, count--;
1129 		processed++;
1130 		wsemul_reset_abortstate(&edp->abortstate);
1131 		break;
1132 	case ABORT_OK:
1133 		/* remove cursor image if visible */
1134 		if (edp->flags & VTFL_CURSORON) {
1135 			rc = (*edp->emulops->cursor)
1136 			    (edp->emulcookie, 0, edp->crow,
1137 			     edp->ccol << edp->dw);
1138 			if (rc != 0)
1139 				return 0;
1140 		}
1141 		break;
1142 	default:
1143 		break;
1144 	}
1145 
1146 	for (;;) {
1147 #ifdef HAVE_JUMP_SCROLL
1148 		switch (edp->abortstate.state) {
1149 		case ABORT_FAILED_JUMP_SCROLL:
1150 			/*
1151 			 * If we failed a previous jump scroll attempt, we
1152 			 * need to try to resume it with the same distance.
1153 			 * We can not recompute it since there might be more
1154 			 * bytes in the tty ring, causing a different result.
1155 			 */
1156 			lines = edp->abortstate.lines;
1157 			break;
1158 		case ABORT_OK:
1159 			/*
1160 			 * If we are at the bottom of the scrolling area, count
1161 			 * newlines until an escape sequence appears.
1162 			 */
1163 			if ((edp->state == VT100_EMUL_STATE_NORMAL || kernel) &&
1164 			    ROWS_BELOW == 0)
1165 				lines = wsemul_vt100_jump_scroll(edp, data,
1166 				    count, kernel);
1167 			else
1168 				lines = 0;
1169 			break;
1170 		default:
1171 			/*
1172 			 * If we are recovering a non-scrolling failure,
1173 			 * do not try to scroll yet.
1174 			 */
1175 			lines = 0;
1176 			break;
1177 		}
1178 
1179 		if (lines > 1) {
1180 			wsemul_resume_abort(&edp->abortstate);
1181 			rc = wsemul_vt100_scrollup(edp, lines);
1182 			if (rc != 0) {
1183 				wsemul_abort_jump_scroll(&edp->abortstate,
1184 				    lines);
1185 				return processed;
1186 			}
1187 			wsemul_reset_abortstate(&edp->abortstate);
1188 			edp->crow -= lines;
1189 		}
1190 #endif
1191 
1192 		wsemul_resume_abort(&edp->abortstate);
1193 
1194 		if (wsemul_getchar(&data, &count, instate,
1195 #ifdef HAVE_UTF8_SUPPORT
1196 		    (edp->state == VT100_EMUL_STATE_NORMAL && !kernel) ?
1197 		      edp->flags & VTFL_UTF8 : 0
1198 #else
1199 		    0
1200 #endif
1201 		    ) != 0)
1202 			break;
1203 
1204 		if (!(instate->inchar & ~0xff) &&
1205 		    (instate->inchar & 0x7f) < 0x20) {
1206 			rc = wsemul_vt100_output_c0c1(edp, instate, kernel);
1207 			if (rc != 0)
1208 				break;
1209 			processed++;
1210  			continue;
1211  		}
1212 
1213 		if (edp->state == VT100_EMUL_STATE_NORMAL || kernel) {
1214 			rc = wsemul_vt100_output_normal(edp, instate, kernel);
1215 			if (rc != 0)
1216 				break;
1217 			processed++;
1218 			continue;
1219 		}
1220 #ifdef DIAGNOSTIC
1221 		if (edp->state > nitems(vt100_output))
1222 			panic("wsemul_vt100: invalid state %d", edp->state);
1223 #endif
1224 		rc = vt100_output[edp->state - 1](edp, instate);
1225 		if (rc != 0)
1226 			break;
1227 		processed++;
1228 	}
1229 
1230 	if (rc != 0)
1231 		wsemul_abort_other(&edp->abortstate);
1232 	else {
1233 		/* put cursor image back if visible */
1234 		if (edp->flags & VTFL_CURSORON) {
1235 			rc = (*edp->emulops->cursor)
1236 			    (edp->emulcookie, 1, edp->crow,
1237 			     edp->ccol << edp->dw);
1238 			if (rc != 0) {
1239 				/*
1240 				 * Fail the last character output, remembering
1241 				 * that only the cursor operation really needs
1242 				 * to be done.
1243 				 */
1244 				wsemul_abort_cursor(&edp->abortstate);
1245 				processed--;
1246 			}
1247 		}
1248 	}
1249 
1250 	if (rc == 0)
1251 		wsemul_reset_abortstate(&edp->abortstate);
1252 
1253 	return processed;
1254 }
1255 
1256 #ifdef HAVE_JUMP_SCROLL
1257 int
1258 wsemul_vt100_jump_scroll(struct wsemul_vt100_emuldata *edp, const u_char *data,
1259     u_int count, int kernel)
1260 {
1261 	struct wsemul_inputstate tmpstate;
1262 	u_int pos, lines;
1263 
1264 	lines = 0;
1265 	pos = edp->ccol;
1266 	tmpstate = kernel ? edp->kstate : edp->instate;	/* structure copy */
1267 
1268 	while (wsemul_getchar(&data, &count, &tmpstate,
1269 #ifdef HAVE_UTF8_SUPPORT
1270 	    kernel ? 0 : edp->flags & VTFL_UTF8
1271 #else
1272 	    0
1273 #endif
1274 	    ) == 0) {
1275 		/*
1276 		 * Only char causing a transition from
1277 		 * VT100_EMUL_STATE_NORMAL to another state, for now.
1278 		 * Revisit this if this changes...
1279 		 */
1280 		if (tmpstate.inchar == ASCII_ESC)
1281 			break;
1282 
1283 		if (ISSET(edp->flags, VTFL_DECAWM))
1284 			switch (tmpstate.inchar) {
1285 			case ASCII_BS:
1286 				if (pos > 0)
1287 					pos--;
1288 				break;
1289 			case ASCII_CR:
1290 				pos = 0;
1291 				break;
1292 			case ASCII_HT:
1293 				if (edp->tabs) {
1294 					pos++;
1295 					while (pos < NCOLS - 1 &&
1296 					    edp->tabs[pos] == 0)
1297 						pos++;
1298 				} else {
1299 					pos = (pos + 7) & ~7;
1300 					if (pos >= NCOLS)
1301 						pos = NCOLS - 1;
1302 				}
1303 				break;
1304 			default:
1305 				if (!(tmpstate.inchar & ~0xff) &&
1306 				    (tmpstate.inchar & 0x7f) < 0x20)
1307 					break;
1308 				if (pos++ >= NCOLS) {
1309 					pos = 0;
1310 					tmpstate.inchar = ASCII_LF;
1311 				}
1312 				break;
1313 			}
1314 
1315 		if (tmpstate.inchar == ASCII_LF ||
1316 		    tmpstate.inchar == ASCII_VT ||
1317 		    tmpstate.inchar == ASCII_FF) {
1318 			if (++lines >= edp->scrreg_nrows - 1)
1319 				break;
1320 		}
1321 	}
1322 
1323 	return lines;
1324 }
1325 #endif
1326