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