1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2008-2009 Ed Schouten <ed@FreeBSD.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 * $FreeBSD$
29 */
30
31static void teken_subr_cursor_up(teken_t *, unsigned int);
32static void teken_subr_erase_line(const teken_t *, unsigned int);
33static void teken_subr_regular_character(teken_t *, teken_char_t);
34static void teken_subr_reset_to_initial_state(teken_t *);
35static void teken_subr_save_cursor(teken_t *);
36
37static inline int
38teken_tab_isset(const teken_t *t, unsigned int col)
39{
40	unsigned int b, o;
41
42	if (col >= T_NUMCOL)
43		return ((col % 8) == 0);
44
45	b = col / (sizeof(unsigned int) * 8);
46	o = col % (sizeof(unsigned int) * 8);
47
48	return (t->t_tabstops[b] & (1U << o));
49}
50
51static inline void
52teken_tab_clear(teken_t *t, unsigned int col)
53{
54	unsigned int b, o;
55
56	if (col >= T_NUMCOL)
57		return;
58
59	b = col / (sizeof(unsigned int) * 8);
60	o = col % (sizeof(unsigned int) * 8);
61
62	t->t_tabstops[b] &= ~(1U << o);
63}
64
65static inline void
66teken_tab_set(teken_t *t, unsigned int col)
67{
68	unsigned int b, o;
69
70	if (col >= T_NUMCOL)
71		return;
72
73	b = col / (sizeof(unsigned int) * 8);
74	o = col % (sizeof(unsigned int) * 8);
75
76	t->t_tabstops[b] |= 1U << o;
77}
78
79static void
80teken_tab_default(teken_t *t)
81{
82	unsigned int i;
83
84	memset(t->t_tabstops, 0, T_NUMCOL / 8);
85
86	for (i = 8; i < T_NUMCOL; i += 8)
87		teken_tab_set(t, i);
88}
89
90static void
91teken_subr_do_scroll(const teken_t *t, int amount)
92{
93	teken_rect_t tr;
94	teken_pos_t tp;
95
96	teken_assert(t->t_cursor.tp_row <= t->t_winsize.tp_row);
97	teken_assert(t->t_scrollreg.ts_end <= t->t_winsize.tp_row);
98	teken_assert(amount != 0);
99
100	/* Copy existing data 1 line up. */
101	if (amount > 0) {
102		/* Scroll down. */
103
104		/* Copy existing data up. */
105		if (t->t_scrollreg.ts_begin + amount < t->t_scrollreg.ts_end) {
106			tr.tr_begin.tp_row = t->t_scrollreg.ts_begin + amount;
107			tr.tr_begin.tp_col = 0;
108			tr.tr_end.tp_row = t->t_scrollreg.ts_end;
109			tr.tr_end.tp_col = t->t_winsize.tp_col;
110			tp.tp_row = t->t_scrollreg.ts_begin;
111			tp.tp_col = 0;
112			teken_funcs_copy(t, &tr, &tp);
113
114			tr.tr_begin.tp_row = t->t_scrollreg.ts_end - amount;
115		} else {
116			tr.tr_begin.tp_row = t->t_scrollreg.ts_begin;
117		}
118
119		/* Clear the last lines. */
120		tr.tr_begin.tp_col = 0;
121		tr.tr_end.tp_row = t->t_scrollreg.ts_end;
122		tr.tr_end.tp_col = t->t_winsize.tp_col;
123		teken_funcs_fill(t, &tr, BLANK, &t->t_curattr);
124	} else {
125		/* Scroll up. */
126		amount = -amount;
127
128		/* Copy existing data down. */
129		if (t->t_scrollreg.ts_begin + amount < t->t_scrollreg.ts_end) {
130			tr.tr_begin.tp_row = t->t_scrollreg.ts_begin;
131			tr.tr_begin.tp_col = 0;
132			tr.tr_end.tp_row = t->t_scrollreg.ts_end - amount;
133			tr.tr_end.tp_col = t->t_winsize.tp_col;
134			tp.tp_row = t->t_scrollreg.ts_begin + amount;
135			tp.tp_col = 0;
136			teken_funcs_copy(t, &tr, &tp);
137
138			tr.tr_end.tp_row = t->t_scrollreg.ts_begin + amount;
139		} else {
140			tr.tr_end.tp_row = t->t_scrollreg.ts_end;
141		}
142
143		/* Clear the first lines. */
144		tr.tr_begin.tp_row = t->t_scrollreg.ts_begin;
145		tr.tr_begin.tp_col = 0;
146		tr.tr_end.tp_col = t->t_winsize.tp_col;
147		teken_funcs_fill(t, &tr, BLANK, &t->t_curattr);
148	}
149}
150
151static ssize_t
152teken_subr_do_cpr(const teken_t *t, unsigned int cmd, char response[16])
153{
154
155	switch (cmd) {
156	case 5: /* Operating status. */
157		strcpy(response, "0n");
158		return (2);
159	case 6: { /* Cursor position. */
160		int len;
161
162		len = snprintf(response, 16, "%u;%uR",
163		    (t->t_cursor.tp_row - t->t_originreg.ts_begin) + 1,
164		    t->t_cursor.tp_col + 1);
165
166		if (len >= 16)
167			return (-1);
168		return (len);
169	}
170	case 15: /* Printer status. */
171		strcpy(response, "13n");
172		return (3);
173	case 25: /* UDK status. */
174		strcpy(response, "20n");
175		return (3);
176	case 26: /* Keyboard status. */
177		strcpy(response, "27;1n");
178		return (5);
179	default:
180		teken_printf("Unknown DSR\n");
181		return (-1);
182	}
183}
184
185static void
186teken_subr_alignment_test(teken_t *t)
187{
188	teken_rect_t tr;
189
190	t->t_cursor.tp_row = t->t_cursor.tp_col = 0;
191	t->t_scrollreg.ts_begin = 0;
192	t->t_scrollreg.ts_end = t->t_winsize.tp_row;
193	t->t_originreg = t->t_scrollreg;
194	t->t_stateflags &= ~(TS_WRAPPED|TS_ORIGIN);
195	teken_funcs_cursor(t);
196
197	tr.tr_begin.tp_row = 0;
198	tr.tr_begin.tp_col = 0;
199	tr.tr_end = t->t_winsize;
200	teken_funcs_fill(t, &tr, 'E', &t->t_defattr);
201}
202
203static void
204teken_subr_backspace(teken_t *t)
205{
206
207	if (t->t_stateflags & TS_CONS25) {
208		if (t->t_cursor.tp_col == 0) {
209			if (t->t_cursor.tp_row == t->t_originreg.ts_begin)
210				return;
211			t->t_cursor.tp_row--;
212			t->t_cursor.tp_col = t->t_winsize.tp_col - 1;
213		} else {
214			t->t_cursor.tp_col--;
215		}
216	} else {
217		if (t->t_cursor.tp_col == 0)
218			return;
219
220		t->t_cursor.tp_col--;
221		t->t_stateflags &= ~TS_WRAPPED;
222	}
223
224	teken_funcs_cursor(t);
225}
226
227static void
228teken_subr_bell(const teken_t *t)
229{
230
231	teken_funcs_bell(t);
232}
233
234static void
235teken_subr_carriage_return(teken_t *t)
236{
237
238	t->t_cursor.tp_col = 0;
239	t->t_stateflags &= ~TS_WRAPPED;
240	teken_funcs_cursor(t);
241}
242
243static void
244teken_subr_cursor_backward(teken_t *t, unsigned int ncols)
245{
246
247	if (ncols > t->t_cursor.tp_col)
248		t->t_cursor.tp_col = 0;
249	else
250		t->t_cursor.tp_col -= ncols;
251	t->t_stateflags &= ~TS_WRAPPED;
252	teken_funcs_cursor(t);
253}
254
255static void
256teken_subr_cursor_backward_tabulation(teken_t *t, unsigned int ntabs)
257{
258
259	do {
260		/* Stop when we've reached the beginning of the line. */
261		if (t->t_cursor.tp_col == 0)
262			break;
263
264		t->t_cursor.tp_col--;
265
266		/* Tab marker set. */
267		if (teken_tab_isset(t, t->t_cursor.tp_col))
268			ntabs--;
269	} while (ntabs > 0);
270
271	teken_funcs_cursor(t);
272}
273
274static void
275teken_subr_cursor_down(teken_t *t, unsigned int nrows)
276{
277
278	if (t->t_cursor.tp_row + nrows >= t->t_scrollreg.ts_end)
279		t->t_cursor.tp_row = t->t_scrollreg.ts_end - 1;
280	else
281		t->t_cursor.tp_row += nrows;
282	t->t_stateflags &= ~TS_WRAPPED;
283	teken_funcs_cursor(t);
284}
285
286static void
287teken_subr_cursor_forward(teken_t *t, unsigned int ncols)
288{
289
290	if (t->t_cursor.tp_col + ncols >= t->t_winsize.tp_col)
291		t->t_cursor.tp_col = t->t_winsize.tp_col - 1;
292	else
293		t->t_cursor.tp_col += ncols;
294	t->t_stateflags &= ~TS_WRAPPED;
295	teken_funcs_cursor(t);
296}
297
298static void
299teken_subr_cursor_forward_tabulation(teken_t *t, unsigned int ntabs)
300{
301
302	do {
303		/* Stop when we've reached the end of the line. */
304		if (t->t_cursor.tp_col == t->t_winsize.tp_col - 1)
305			break;
306
307		t->t_cursor.tp_col++;
308
309		/* Tab marker set. */
310		if (teken_tab_isset(t, t->t_cursor.tp_col))
311			ntabs--;
312	} while (ntabs > 0);
313
314	teken_funcs_cursor(t);
315}
316
317static void
318teken_subr_cursor_next_line(teken_t *t, unsigned int ncols)
319{
320
321	t->t_cursor.tp_col = 0;
322	teken_subr_cursor_down(t, ncols);
323}
324
325static void
326teken_subr_cursor_position(teken_t *t, unsigned int row, unsigned int col)
327{
328
329	row = (row - 1) + t->t_originreg.ts_begin;
330	t->t_cursor.tp_row = row < t->t_originreg.ts_end ?
331	    row : t->t_originreg.ts_end - 1;
332
333	col--;
334	t->t_cursor.tp_col = col < t->t_winsize.tp_col ?
335	    col : t->t_winsize.tp_col - 1;
336
337	t->t_stateflags &= ~TS_WRAPPED;
338	teken_funcs_cursor(t);
339}
340
341static void
342teken_subr_cursor_position_report(const teken_t *t, unsigned int cmd)
343{
344	char response[18] = "\x1B[";
345	ssize_t len;
346
347	len = teken_subr_do_cpr(t, cmd, response + 2);
348	if (len < 0)
349		return;
350
351	teken_funcs_respond(t, response, len + 2);
352}
353
354static void
355teken_subr_cursor_previous_line(teken_t *t, unsigned int ncols)
356{
357
358	t->t_cursor.tp_col = 0;
359	teken_subr_cursor_up(t, ncols);
360}
361
362static void
363teken_subr_cursor_up(teken_t *t, unsigned int nrows)
364{
365
366	if (t->t_scrollreg.ts_begin + nrows >= t->t_cursor.tp_row)
367		t->t_cursor.tp_row = t->t_scrollreg.ts_begin;
368	else
369		t->t_cursor.tp_row -= nrows;
370	t->t_stateflags &= ~TS_WRAPPED;
371	teken_funcs_cursor(t);
372}
373
374static void
375teken_subr_set_cursor_style(teken_t *t __unused, unsigned int style __unused)
376{
377
378	/* TODO */
379
380	/*
381	 * CSI Ps SP q
382	 *   Set cursor style (DECSCUSR), VT520.
383	 *     Ps = 0  -> blinking block.
384	 *     Ps = 1  -> blinking block (default).
385	 *     Ps = 2  -> steady block.
386	 *     Ps = 3  -> blinking underline.
387	 *     Ps = 4  -> steady underline.
388	 *     Ps = 5  -> blinking bar (xterm).
389	 *     Ps = 6  -> steady bar (xterm).
390	 */
391}
392
393static void
394teken_subr_delete_character(const teken_t *t, unsigned int ncols)
395{
396	teken_rect_t tr;
397
398	tr.tr_begin.tp_row = t->t_cursor.tp_row;
399	tr.tr_end.tp_row = t->t_cursor.tp_row + 1;
400	tr.tr_end.tp_col = t->t_winsize.tp_col;
401
402	if (t->t_cursor.tp_col + ncols >= t->t_winsize.tp_col) {
403		tr.tr_begin.tp_col = t->t_cursor.tp_col;
404	} else {
405		/* Copy characters to the left. */
406		tr.tr_begin.tp_col = t->t_cursor.tp_col + ncols;
407		teken_funcs_copy(t, &tr, &t->t_cursor);
408
409		tr.tr_begin.tp_col = t->t_winsize.tp_col - ncols;
410	}
411
412	/* Blank trailing columns. */
413	teken_funcs_fill(t, &tr, BLANK, &t->t_curattr);
414}
415
416static void
417teken_subr_delete_line(const teken_t *t, unsigned int nrows)
418{
419	teken_rect_t tr;
420
421	/* Ignore if outside scrolling region. */
422	if (t->t_cursor.tp_row < t->t_scrollreg.ts_begin ||
423	    t->t_cursor.tp_row >= t->t_scrollreg.ts_end)
424		return;
425
426	tr.tr_begin.tp_col = 0;
427	tr.tr_end.tp_row = t->t_scrollreg.ts_end;
428	tr.tr_end.tp_col = t->t_winsize.tp_col;
429
430	if (t->t_cursor.tp_row + nrows >= t->t_scrollreg.ts_end) {
431		tr.tr_begin.tp_row = t->t_cursor.tp_row;
432	} else {
433		teken_pos_t tp;
434
435		/* Copy rows up. */
436		tr.tr_begin.tp_row = t->t_cursor.tp_row + nrows;
437		tp.tp_row = t->t_cursor.tp_row;
438		tp.tp_col = 0;
439		teken_funcs_copy(t, &tr, &tp);
440
441		tr.tr_begin.tp_row = t->t_scrollreg.ts_end - nrows;
442	}
443
444	/* Blank trailing rows. */
445	teken_funcs_fill(t, &tr, BLANK, &t->t_curattr);
446}
447
448static void
449teken_subr_device_control_string(teken_t *t)
450{
451
452	teken_printf("Unsupported device control string\n");
453	t->t_stateflags |= TS_INSTRING;
454}
455
456static void
457teken_subr_device_status_report(const teken_t *t, unsigned int cmd)
458{
459	char response[19] = "\x1B[?";
460	ssize_t len;
461
462	len = teken_subr_do_cpr(t, cmd, response + 3);
463	if (len < 0)
464		return;
465
466	teken_funcs_respond(t, response, len + 3);
467}
468
469static void
470teken_subr_double_height_double_width_line_top(const teken_t *t)
471{
472
473	(void)t;
474	teken_printf("double height double width top\n");
475}
476
477static void
478teken_subr_double_height_double_width_line_bottom(const teken_t *t)
479{
480
481	(void)t;
482	teken_printf("double height double width bottom\n");
483}
484
485static void
486teken_subr_erase_character(const teken_t *t, unsigned int ncols)
487{
488	teken_rect_t tr;
489
490	tr.tr_begin = t->t_cursor;
491	tr.tr_end.tp_row = t->t_cursor.tp_row + 1;
492
493	if (t->t_cursor.tp_col + ncols >= t->t_winsize.tp_col)
494		tr.tr_end.tp_col = t->t_winsize.tp_col;
495	else
496		tr.tr_end.tp_col = t->t_cursor.tp_col + ncols;
497
498	teken_funcs_fill(t, &tr, BLANK, &t->t_curattr);
499}
500
501static void
502teken_subr_erase_display(const teken_t *t, unsigned int mode)
503{
504	teken_rect_t r;
505
506	r.tr_begin.tp_col = 0;
507	r.tr_end.tp_col = t->t_winsize.tp_col;
508
509	switch (mode) {
510	case 1: /* Erase from the top to the cursor. */
511		teken_subr_erase_line(t, 1);
512
513		/* Erase lines above. */
514		if (t->t_cursor.tp_row == 0)
515			return;
516		r.tr_begin.tp_row = 0;
517		r.tr_end.tp_row = t->t_cursor.tp_row;
518		break;
519	case 2: /* Erase entire display. */
520		r.tr_begin.tp_row = 0;
521		r.tr_end.tp_row = t->t_winsize.tp_row;
522		break;
523	default: /* Erase from cursor to the bottom. */
524		teken_subr_erase_line(t, 0);
525
526		/* Erase lines below. */
527		if (t->t_cursor.tp_row == t->t_winsize.tp_row - 1)
528			return;
529		r.tr_begin.tp_row = t->t_cursor.tp_row + 1;
530		r.tr_end.tp_row = t->t_winsize.tp_row;
531		break;
532	}
533
534	teken_funcs_fill(t, &r, BLANK, &t->t_curattr);
535}
536
537static void
538teken_subr_erase_line(const teken_t *t, unsigned int mode)
539{
540	teken_rect_t r;
541
542	r.tr_begin.tp_row = t->t_cursor.tp_row;
543	r.tr_end.tp_row = t->t_cursor.tp_row + 1;
544
545	switch (mode) {
546	case 1: /* Erase from the beginning of the line to the cursor. */
547		r.tr_begin.tp_col = 0;
548		r.tr_end.tp_col = t->t_cursor.tp_col + 1;
549		break;
550	case 2: /* Erase entire line. */
551		r.tr_begin.tp_col = 0;
552		r.tr_end.tp_col = t->t_winsize.tp_col;
553		break;
554	default: /* Erase from cursor to the end of the line. */
555		r.tr_begin.tp_col = t->t_cursor.tp_col;
556		r.tr_end.tp_col = t->t_winsize.tp_col;
557		break;
558	}
559
560	teken_funcs_fill(t, &r, BLANK, &t->t_curattr);
561}
562
563static void
564teken_subr_g0_scs_special_graphics(teken_t *t)
565{
566
567	t->t_scs[0] = teken_scs_special_graphics;
568}
569
570static void
571teken_subr_g0_scs_uk_national(teken_t *t)
572{
573
574	t->t_scs[0] = teken_scs_uk_national;
575}
576
577static void
578teken_subr_g0_scs_us_ascii(teken_t *t)
579{
580
581	t->t_scs[0] = teken_scs_us_ascii;
582}
583
584static void
585teken_subr_g1_scs_special_graphics(teken_t *t)
586{
587
588	t->t_scs[1] = teken_scs_special_graphics;
589}
590
591static void
592teken_subr_g1_scs_uk_national(teken_t *t)
593{
594
595	t->t_scs[1] = teken_scs_uk_national;
596}
597
598static void
599teken_subr_g1_scs_us_ascii(teken_t *t)
600{
601
602	t->t_scs[1] = teken_scs_us_ascii;
603}
604
605static void
606teken_subr_horizontal_position_absolute(teken_t *t, unsigned int col)
607{
608
609	col--;
610	t->t_cursor.tp_col = col < t->t_winsize.tp_col ?
611	    col : t->t_winsize.tp_col - 1;
612
613	t->t_stateflags &= ~TS_WRAPPED;
614	teken_funcs_cursor(t);
615}
616
617static void
618teken_subr_horizontal_tab(teken_t *t)
619{
620
621	teken_subr_cursor_forward_tabulation(t, 1);
622}
623
624static void
625teken_subr_horizontal_tab_set(teken_t *t)
626{
627
628	teken_tab_set(t, t->t_cursor.tp_col);
629}
630
631static void
632teken_subr_index(teken_t *t)
633{
634
635	if (t->t_cursor.tp_row < t->t_scrollreg.ts_end - 1) {
636		t->t_cursor.tp_row++;
637		t->t_stateflags &= ~TS_WRAPPED;
638		teken_funcs_cursor(t);
639	} else {
640		teken_subr_do_scroll(t, 1);
641	}
642}
643
644static void
645teken_subr_insert_character(const teken_t *t, unsigned int ncols)
646{
647	teken_rect_t tr;
648
649	tr.tr_begin = t->t_cursor;
650	tr.tr_end.tp_row = t->t_cursor.tp_row + 1;
651
652	if (t->t_cursor.tp_col + ncols >= t->t_winsize.tp_col) {
653		tr.tr_end.tp_col = t->t_winsize.tp_col;
654	} else {
655		teken_pos_t tp;
656
657		/* Copy characters to the right. */
658		tr.tr_end.tp_col = t->t_winsize.tp_col - ncols;
659		tp.tp_row = t->t_cursor.tp_row;
660		tp.tp_col = t->t_cursor.tp_col + ncols;
661		teken_funcs_copy(t, &tr, &tp);
662
663		tr.tr_end.tp_col = t->t_cursor.tp_col + ncols;
664	}
665
666	/* Blank current location. */
667	teken_funcs_fill(t, &tr, BLANK, &t->t_curattr);
668}
669
670static void
671teken_subr_insert_line(const teken_t *t, unsigned int nrows)
672{
673	teken_rect_t tr;
674
675	/* Ignore if outside scrolling region. */
676	if (t->t_cursor.tp_row < t->t_scrollreg.ts_begin ||
677	    t->t_cursor.tp_row >= t->t_scrollreg.ts_end)
678		return;
679
680	tr.tr_begin.tp_row = t->t_cursor.tp_row;
681	tr.tr_begin.tp_col = 0;
682	tr.tr_end.tp_col = t->t_winsize.tp_col;
683
684	if (t->t_cursor.tp_row + nrows >= t->t_scrollreg.ts_end) {
685		tr.tr_end.tp_row = t->t_scrollreg.ts_end;
686	} else {
687		teken_pos_t tp;
688
689		/* Copy lines down. */
690		tr.tr_end.tp_row = t->t_scrollreg.ts_end - nrows;
691		tp.tp_row = t->t_cursor.tp_row + nrows;
692		tp.tp_col = 0;
693		teken_funcs_copy(t, &tr, &tp);
694
695		tr.tr_end.tp_row = t->t_cursor.tp_row + nrows;
696	}
697
698	/* Blank current location. */
699	teken_funcs_fill(t, &tr, BLANK, &t->t_curattr);
700}
701
702static void
703teken_subr_keypad_application_mode(const teken_t *t)
704{
705
706	teken_funcs_param(t, TP_KEYPADAPP, 1);
707}
708
709static void
710teken_subr_keypad_numeric_mode(const teken_t *t)
711{
712
713	teken_funcs_param(t, TP_KEYPADAPP, 0);
714}
715
716static void
717teken_subr_newline(teken_t *t)
718{
719
720	t->t_cursor.tp_row++;
721
722	if (t->t_cursor.tp_row >= t->t_scrollreg.ts_end) {
723		teken_subr_do_scroll(t, 1);
724		t->t_cursor.tp_row = t->t_scrollreg.ts_end - 1;
725	}
726
727	t->t_stateflags &= ~TS_WRAPPED;
728	teken_funcs_cursor(t);
729}
730
731static void
732teken_subr_newpage(teken_t *t)
733{
734
735	if (t->t_stateflags & TS_CONS25) {
736		teken_rect_t tr;
737
738		/* Clear screen. */
739		tr.tr_begin.tp_row = t->t_originreg.ts_begin;
740		tr.tr_begin.tp_col = 0;
741		tr.tr_end.tp_row = t->t_originreg.ts_end;
742		tr.tr_end.tp_col = t->t_winsize.tp_col;
743		teken_funcs_fill(t, &tr, BLANK, &t->t_curattr);
744
745		/* Cursor at top left. */
746		t->t_cursor.tp_row = t->t_originreg.ts_begin;
747		t->t_cursor.tp_col = 0;
748		t->t_stateflags &= ~TS_WRAPPED;
749		teken_funcs_cursor(t);
750	} else {
751		teken_subr_newline(t);
752	}
753}
754
755static void
756teken_subr_next_line(teken_t *t)
757{
758
759	t->t_cursor.tp_col = 0;
760	teken_subr_newline(t);
761}
762
763static void
764teken_subr_operating_system_command(teken_t *t)
765{
766
767	teken_printf("Unsupported operating system command\n");
768	t->t_stateflags |= TS_INSTRING;
769}
770
771static void
772teken_subr_pan_down(const teken_t *t, unsigned int nrows)
773{
774
775	teken_subr_do_scroll(t, (int)nrows);
776}
777
778static void
779teken_subr_pan_up(const teken_t *t, unsigned int nrows)
780{
781
782	teken_subr_do_scroll(t, -(int)nrows);
783}
784
785static void
786teken_subr_primary_device_attributes(const teken_t *t, unsigned int request)
787{
788
789	if (request == 0) {
790		const char response[] = "\x1B[?1;2c";
791
792		teken_funcs_respond(t, response, sizeof response - 1);
793	} else {
794		teken_printf("Unknown DA1\n");
795	}
796}
797
798static void
799teken_subr_do_putchar(teken_t *t, const teken_pos_t *tp, teken_char_t c,
800    int width)
801{
802
803	t->t_last = c;
804	if (t->t_stateflags & TS_INSERT &&
805	    tp->tp_col < t->t_winsize.tp_col - width) {
806		teken_rect_t ctr;
807		teken_pos_t ctp;
808
809		/* Insert mode. Move existing characters to the right. */
810		ctr.tr_begin = *tp;
811		ctr.tr_end.tp_row = tp->tp_row + 1;
812		ctr.tr_end.tp_col = t->t_winsize.tp_col - width;
813		ctp.tp_row = tp->tp_row;
814		ctp.tp_col = tp->tp_col + width;
815		teken_funcs_copy(t, &ctr, &ctp);
816	}
817
818	teken_funcs_putchar(t, tp, c, &t->t_curattr);
819
820	if (width == 2 && tp->tp_col + 1 < t->t_winsize.tp_col) {
821		teken_pos_t tp2;
822		teken_attr_t attr;
823
824		/* Print second half of CJK fullwidth character. */
825		tp2.tp_row = tp->tp_row;
826		tp2.tp_col = tp->tp_col + 1;
827		attr = t->t_curattr;
828		attr.ta_format |= TF_CJK_RIGHT;
829		teken_funcs_putchar(t, &tp2, c, &attr);
830	}
831}
832
833static void
834teken_subr_regular_character(teken_t *t, teken_char_t c)
835{
836	int width;
837
838	if (t->t_stateflags & TS_8BIT) {
839		if (!(t->t_stateflags & TS_CONS25) && (c <= 0x1b || c == 0x7f))
840			return;
841		c = teken_scs_process(t, c);
842		width = 1;
843	} else {
844		c = teken_scs_process(t, c);
845		width = teken_wcwidth(c);
846		/* XXX: Don't process zero-width characters yet. */
847		if (width <= 0)
848			return;
849	}
850
851	if (t->t_stateflags & TS_CONS25) {
852		teken_subr_do_putchar(t, &t->t_cursor, c, width);
853		t->t_cursor.tp_col += width;
854
855		if (t->t_cursor.tp_col >= t->t_winsize.tp_col) {
856			if (t->t_cursor.tp_row == t->t_scrollreg.ts_end - 1) {
857				/* Perform scrolling. */
858				teken_subr_do_scroll(t, 1);
859			} else {
860				/* No scrolling needed. */
861				if (t->t_cursor.tp_row <
862				    t->t_winsize.tp_row - 1)
863					t->t_cursor.tp_row++;
864			}
865			t->t_cursor.tp_col = 0;
866		}
867	} else if (t->t_stateflags & TS_AUTOWRAP &&
868	    ((t->t_stateflags & TS_WRAPPED &&
869	    t->t_cursor.tp_col + 1 == t->t_winsize.tp_col) ||
870	    t->t_cursor.tp_col + width > t->t_winsize.tp_col)) {
871		teken_pos_t tp;
872
873		/*
874		 * Perform line wrapping, if:
875		 * - Autowrapping is enabled, and
876		 *   - We're in the wrapped state at the last column, or
877		 *   - The character to be printed does not fit anymore.
878		 */
879		if (t->t_cursor.tp_row == t->t_scrollreg.ts_end - 1) {
880			/* Perform scrolling. */
881			teken_subr_do_scroll(t, 1);
882			tp.tp_row = t->t_scrollreg.ts_end - 1;
883		} else {
884			/* No scrolling needed. */
885			tp.tp_row = t->t_cursor.tp_row + 1;
886			if (tp.tp_row == t->t_winsize.tp_row) {
887				/*
888				 * Corner case: regular character
889				 * outside scrolling region, but at the
890				 * bottom of the screen.
891				 */
892				teken_subr_do_putchar(t, &t->t_cursor,
893				    c, width);
894				return;
895			}
896		}
897
898		tp.tp_col = 0;
899		teken_subr_do_putchar(t, &tp, c, width);
900
901		t->t_cursor.tp_row = tp.tp_row;
902		t->t_cursor.tp_col = width;
903		t->t_stateflags &= ~TS_WRAPPED;
904	} else {
905		/* No line wrapping needed. */
906		teken_subr_do_putchar(t, &t->t_cursor, c, width);
907		t->t_cursor.tp_col += width;
908
909		if (t->t_cursor.tp_col >= t->t_winsize.tp_col) {
910			t->t_stateflags |= TS_WRAPPED;
911			t->t_cursor.tp_col = t->t_winsize.tp_col - 1;
912		} else {
913			t->t_stateflags &= ~TS_WRAPPED;
914		}
915	}
916
917	teken_funcs_cursor(t);
918}
919
920static void
921teken_subr_reset_dec_mode(teken_t *t, unsigned int cmd)
922{
923
924	switch (cmd) {
925	case 1: /* Cursor keys mode. */
926		t->t_stateflags &= ~TS_CURSORKEYS;
927		break;
928	case 2: /* DECANM: ANSI/VT52 mode. */
929		teken_printf("DECRST VT52\n");
930		break;
931	case 3: /* 132 column mode. */
932		teken_funcs_param(t, TP_132COLS, 0);
933		teken_subr_reset_to_initial_state(t);
934		break;
935	case 5: /* Inverse video. */
936		teken_printf("DECRST inverse video\n");
937		break;
938	case 6: /* Origin mode. */
939		t->t_stateflags &= ~TS_ORIGIN;
940		t->t_originreg.ts_begin = 0;
941		t->t_originreg.ts_end = t->t_winsize.tp_row;
942		t->t_cursor.tp_row = t->t_cursor.tp_col = 0;
943		t->t_stateflags &= ~TS_WRAPPED;
944		teken_funcs_cursor(t);
945		break;
946	case 7: /* Autowrap mode. */
947		t->t_stateflags &= ~TS_AUTOWRAP;
948		break;
949	case 8: /* Autorepeat mode. */
950		teken_funcs_param(t, TP_AUTOREPEAT, 0);
951		break;
952	case 25: /* Hide cursor. */
953		teken_funcs_param(t, TP_SHOWCURSOR, 0);
954		break;
955	case 40: /* Disallow 132 columns. */
956		teken_printf("DECRST allow 132\n");
957		break;
958	case 45: /* Disable reverse wraparound. */
959		teken_printf("DECRST reverse wraparound\n");
960		break;
961	case 47: /* Switch to alternate buffer. */
962		teken_printf("Switch to alternate buffer\n");
963		break;
964	case 1000: /* Mouse input. */
965		teken_funcs_param(t, TP_MOUSE, 0);
966		break;
967	default:
968		teken_printf("Unknown DECRST: %u\n", cmd);
969	}
970}
971
972static void
973teken_subr_reset_mode(teken_t *t, unsigned int cmd)
974{
975
976	switch (cmd) {
977	case 4:
978		t->t_stateflags &= ~TS_INSERT;
979		break;
980	default:
981		teken_printf("Unknown reset mode: %u\n", cmd);
982	}
983}
984
985static void
986teken_subr_do_resize(teken_t *t)
987{
988
989	t->t_scrollreg.ts_begin = 0;
990	t->t_scrollreg.ts_end = t->t_winsize.tp_row;
991	t->t_originreg = t->t_scrollreg;
992}
993
994static void
995teken_subr_do_reset(teken_t *t)
996{
997
998	t->t_curattr = t->t_defattr;
999	t->t_cursor.tp_row = t->t_cursor.tp_col = 0;
1000	t->t_scrollreg.ts_begin = 0;
1001	t->t_scrollreg.ts_end = t->t_winsize.tp_row;
1002	t->t_originreg = t->t_scrollreg;
1003	t->t_stateflags &= TS_8BIT | TS_CONS25 | TS_CONS25KEYS;
1004	t->t_stateflags |= TS_AUTOWRAP;
1005
1006	t->t_scs[0] = teken_scs_us_ascii;
1007	t->t_scs[1] = teken_scs_us_ascii;
1008	t->t_curscs = 0;
1009
1010	teken_subr_save_cursor(t);
1011	teken_tab_default(t);
1012}
1013
1014static void
1015teken_subr_reset_to_initial_state(teken_t *t)
1016{
1017
1018	teken_subr_do_reset(t);
1019	teken_subr_erase_display(t, 2);
1020	teken_funcs_param(t, TP_SHOWCURSOR, 1);
1021	teken_funcs_cursor(t);
1022}
1023
1024static void
1025teken_subr_restore_cursor(teken_t *t)
1026{
1027
1028	t->t_cursor = t->t_saved_cursor;
1029	t->t_curattr = t->t_saved_curattr;
1030	t->t_scs[t->t_curscs] = t->t_saved_curscs;
1031	t->t_stateflags &= ~TS_WRAPPED;
1032
1033	/* Get out of origin mode when the cursor is moved outside. */
1034	if (t->t_cursor.tp_row < t->t_originreg.ts_begin ||
1035	    t->t_cursor.tp_row >= t->t_originreg.ts_end) {
1036		t->t_stateflags &= ~TS_ORIGIN;
1037		t->t_originreg.ts_begin = 0;
1038		t->t_originreg.ts_end = t->t_winsize.tp_row;
1039	}
1040
1041	teken_funcs_cursor(t);
1042}
1043
1044static void
1045teken_subr_reverse_index(teken_t *t)
1046{
1047
1048	if (t->t_cursor.tp_row > t->t_scrollreg.ts_begin) {
1049		t->t_cursor.tp_row--;
1050		t->t_stateflags &= ~TS_WRAPPED;
1051		teken_funcs_cursor(t);
1052	} else {
1053		teken_subr_do_scroll(t, -1);
1054	}
1055}
1056
1057static void
1058teken_subr_save_cursor(teken_t *t)
1059{
1060
1061	t->t_saved_cursor = t->t_cursor;
1062	t->t_saved_curattr = t->t_curattr;
1063	t->t_saved_curscs = t->t_scs[t->t_curscs];
1064}
1065
1066static void
1067teken_subr_secondary_device_attributes(const teken_t *t, unsigned int request)
1068{
1069
1070	if (request == 0) {
1071		const char response[] = "\x1B[>0;10;0c";
1072		teken_funcs_respond(t, response, sizeof response - 1);
1073	} else {
1074		teken_printf("Unknown DA2\n");
1075	}
1076}
1077
1078static void
1079teken_subr_set_dec_mode(teken_t *t, unsigned int cmd)
1080{
1081
1082	switch (cmd) {
1083	case 1: /* Cursor keys mode. */
1084		t->t_stateflags |= TS_CURSORKEYS;
1085		break;
1086	case 2: /* DECANM: ANSI/VT52 mode. */
1087		teken_printf("DECSET VT52\n");
1088		break;
1089	case 3: /* 132 column mode. */
1090		teken_funcs_param(t, TP_132COLS, 1);
1091		teken_subr_reset_to_initial_state(t);
1092		break;
1093	case 5: /* Inverse video. */
1094		teken_printf("DECSET inverse video\n");
1095		break;
1096	case 6: /* Origin mode. */
1097		t->t_stateflags |= TS_ORIGIN;
1098		t->t_originreg = t->t_scrollreg;
1099		t->t_cursor.tp_row = t->t_scrollreg.ts_begin;
1100		t->t_cursor.tp_col = 0;
1101		t->t_stateflags &= ~TS_WRAPPED;
1102		teken_funcs_cursor(t);
1103		break;
1104	case 7: /* Autowrap mode. */
1105		t->t_stateflags |= TS_AUTOWRAP;
1106		break;
1107	case 8: /* Autorepeat mode. */
1108		teken_funcs_param(t, TP_AUTOREPEAT, 1);
1109		break;
1110	case 25: /* Display cursor. */
1111		teken_funcs_param(t, TP_SHOWCURSOR, 1);
1112		break;
1113	case 40: /* Allow 132 columns. */
1114		teken_printf("DECSET allow 132\n");
1115		break;
1116	case 45: /* Enable reverse wraparound. */
1117		teken_printf("DECSET reverse wraparound\n");
1118		break;
1119	case 47: /* Switch to alternate buffer. */
1120		teken_printf("Switch away from alternate buffer\n");
1121		break;
1122	case 1000: /* Mouse input. */
1123		teken_funcs_param(t, TP_MOUSE, 1);
1124		break;
1125	default:
1126		teken_printf("Unknown DECSET: %u\n", cmd);
1127	}
1128}
1129
1130static void
1131teken_subr_set_mode(teken_t *t, unsigned int cmd)
1132{
1133
1134	switch (cmd) {
1135	case 4:
1136		teken_printf("Insert mode\n");
1137		t->t_stateflags |= TS_INSERT;
1138		break;
1139	default:
1140		teken_printf("Unknown set mode: %u\n", cmd);
1141	}
1142}
1143
1144static void
1145teken_subr_set_graphic_rendition(teken_t *t, unsigned int ncmds,
1146    const unsigned int cmds[])
1147{
1148	unsigned int i, n;
1149
1150	/* No attributes means reset. */
1151	if (ncmds == 0) {
1152		t->t_curattr = t->t_defattr;
1153		return;
1154	}
1155
1156	for (i = 0; i < ncmds; i++) {
1157		n = cmds[i];
1158
1159		switch (n) {
1160		case 0: /* Reset. */
1161			t->t_curattr = t->t_defattr;
1162			break;
1163		case 1: /* Bold. */
1164			t->t_curattr.ta_format |= TF_BOLD;
1165			break;
1166		case 4: /* Underline. */
1167			t->t_curattr.ta_format |= TF_UNDERLINE;
1168			break;
1169		case 5: /* Blink. */
1170			t->t_curattr.ta_format |= TF_BLINK;
1171			break;
1172		case 7: /* Reverse. */
1173			t->t_curattr.ta_format |= TF_REVERSE;
1174			break;
1175		case 22: /* Remove bold. */
1176			t->t_curattr.ta_format &= ~TF_BOLD;
1177			break;
1178		case 24: /* Remove underline. */
1179			t->t_curattr.ta_format &= ~TF_UNDERLINE;
1180			break;
1181		case 25: /* Remove blink. */
1182			t->t_curattr.ta_format &= ~TF_BLINK;
1183			break;
1184		case 27: /* Remove reverse. */
1185			t->t_curattr.ta_format &= ~TF_REVERSE;
1186			break;
1187		case 30: /* Set foreground color: black */
1188		case 31: /* Set foreground color: red */
1189		case 32: /* Set foreground color: green */
1190		case 33: /* Set foreground color: brown */
1191		case 34: /* Set foreground color: blue */
1192		case 35: /* Set foreground color: magenta */
1193		case 36: /* Set foreground color: cyan */
1194		case 37: /* Set foreground color: white */
1195			t->t_curattr.ta_fgcolor = n - 30;
1196			break;
1197		case 38: /* Set foreground color: 256 color mode */
1198			if (i + 2 >= ncmds || cmds[i + 1] != 5)
1199				continue;
1200			t->t_curattr.ta_fgcolor = cmds[i + 2];
1201			i += 2;
1202			break;
1203		case 39: /* Set default foreground color. */
1204			t->t_curattr.ta_fgcolor = t->t_defattr.ta_fgcolor;
1205			break;
1206		case 40: /* Set background color: black */
1207		case 41: /* Set background color: red */
1208		case 42: /* Set background color: green */
1209		case 43: /* Set background color: brown */
1210		case 44: /* Set background color: blue */
1211		case 45: /* Set background color: magenta */
1212		case 46: /* Set background color: cyan */
1213		case 47: /* Set background color: white */
1214			t->t_curattr.ta_bgcolor = n - 40;
1215			break;
1216		case 48: /* Set background color: 256 color mode */
1217			if (i + 2 >= ncmds || cmds[i + 1] != 5)
1218				continue;
1219			t->t_curattr.ta_bgcolor = cmds[i + 2];
1220			i += 2;
1221			break;
1222		case 49: /* Set default background color. */
1223			t->t_curattr.ta_bgcolor = t->t_defattr.ta_bgcolor;
1224			break;
1225		case 90: /* Set bright foreground color: black */
1226		case 91: /* Set bright foreground color: red */
1227		case 92: /* Set bright foreground color: green */
1228		case 93: /* Set bright foreground color: brown */
1229		case 94: /* Set bright foreground color: blue */
1230		case 95: /* Set bright foreground color: magenta */
1231		case 96: /* Set bright foreground color: cyan */
1232		case 97: /* Set bright foreground color: white */
1233			t->t_curattr.ta_fgcolor = (n - 90) + 8;
1234			break;
1235		case 100: /* Set bright background color: black */
1236		case 101: /* Set bright background color: red */
1237		case 102: /* Set bright background color: green */
1238		case 103: /* Set bright background color: brown */
1239		case 104: /* Set bright background color: blue */
1240		case 105: /* Set bright background color: magenta */
1241		case 106: /* Set bright background color: cyan */
1242		case 107: /* Set bright background color: white */
1243			t->t_curattr.ta_bgcolor = (n - 100) + 8;
1244			break;
1245		default:
1246			teken_printf("unsupported attribute %u\n", n);
1247		}
1248	}
1249}
1250
1251static void
1252teken_subr_set_top_and_bottom_margins(teken_t *t, unsigned int top,
1253    unsigned int bottom)
1254{
1255
1256	/* Adjust top row number. */
1257	if (top > 0)
1258		top--;
1259	/* Adjust bottom row number. */
1260	if (bottom == 0 || bottom > t->t_winsize.tp_row)
1261		bottom = t->t_winsize.tp_row;
1262
1263	/* Invalid arguments. */
1264	if (top >= bottom - 1) {
1265		top = 0;
1266		bottom = t->t_winsize.tp_row;
1267	}
1268
1269	/* Apply scrolling region. */
1270	t->t_scrollreg.ts_begin = top;
1271	t->t_scrollreg.ts_end = bottom;
1272	if (t->t_stateflags & TS_ORIGIN)
1273		t->t_originreg = t->t_scrollreg;
1274
1275	/* Home cursor to the top left of the scrolling region. */
1276	t->t_cursor.tp_row = t->t_originreg.ts_begin;
1277	t->t_cursor.tp_col = 0;
1278	t->t_stateflags &= ~TS_WRAPPED;
1279	teken_funcs_cursor(t);
1280}
1281
1282static void
1283teken_subr_single_height_double_width_line(const teken_t *t)
1284{
1285
1286	(void)t;
1287	teken_printf("single height double width???\n");
1288}
1289
1290static void
1291teken_subr_single_height_single_width_line(const teken_t *t)
1292{
1293
1294	(void)t;
1295	teken_printf("single height single width???\n");
1296}
1297
1298static void
1299teken_subr_string_terminator(const teken_t *t)
1300{
1301
1302	(void)t;
1303	/*
1304	 * Strings are already terminated in teken_input_char() when ^[
1305	 * is inserted.
1306	 */
1307}
1308
1309static void
1310teken_subr_tab_clear(teken_t *t, unsigned int cmd)
1311{
1312
1313	switch (cmd) {
1314	case 0:
1315		teken_tab_clear(t, t->t_cursor.tp_col);
1316		break;
1317	case 3:
1318		memset(t->t_tabstops, 0, T_NUMCOL / 8);
1319		break;
1320	default:
1321		break;
1322	}
1323}
1324
1325static void
1326teken_subr_vertical_position_absolute(teken_t *t, unsigned int row)
1327{
1328
1329	row = (row - 1) + t->t_originreg.ts_begin;
1330	t->t_cursor.tp_row = row < t->t_originreg.ts_end ?
1331	    row : t->t_originreg.ts_end - 1;
1332
1333	t->t_stateflags &= ~TS_WRAPPED;
1334	teken_funcs_cursor(t);
1335}
1336
1337static void
1338teken_subr_repeat_last_graphic_char(teken_t *t, unsigned int rpts)
1339{
1340	unsigned int max_repetitions;
1341
1342	max_repetitions = t->t_winsize.tp_row * t->t_winsize.tp_col;
1343	if (rpts > max_repetitions)
1344		rpts = max_repetitions;
1345	for (; t->t_last != 0 && rpts > 0; rpts--)
1346		teken_subr_regular_character(t, t->t_last);
1347}
1348