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