xref: /netbsd-src/sys/dev/rcons/rcons_subr.c (revision aaf4ece63a859a04e37cf3a7229b5fab0157cc06)
1 /*	$NetBSD: rcons_subr.c,v 1.15 2005/12/11 12:23:44 christos Exp $ */
2 
3 /*
4  * Copyright (c) 1991, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This software was developed by the Computer Systems Engineering group
8  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
9  * contributed to Berkeley.
10  *
11  * All advertising materials mentioning features or use of this software
12  * must display the following acknowledgement:
13  *	This product includes software developed by the University of
14  *	California, Lawrence Berkeley Laboratory.
15  *
16  * Redistribution and use in source and binary forms, with or without
17  * modification, are permitted provided that the following conditions
18  * are met:
19  * 1. Redistributions of source code must retain the above copyright
20  *    notice, this list of conditions and the following disclaimer.
21  * 2. Redistributions in binary form must reproduce the above copyright
22  *    notice, this list of conditions and the following disclaimer in the
23  *    documentation and/or other materials provided with the distribution.
24  * 3. Neither the name of the University nor the names of its contributors
25  *    may be used to endorse or promote products derived from this software
26  *    without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38  * SUCH DAMAGE.
39  *
40  *	@(#)rcons_subr.c	8.1 (Berkeley) 6/11/93
41  */
42 
43 #include <sys/cdefs.h>
44 __KERNEL_RCSID(0, "$NetBSD: rcons_subr.c,v 1.15 2005/12/11 12:23:44 christos Exp $");
45 
46 #include <sys/param.h>
47 #ifdef _KERNEL
48 #include <sys/device.h>
49 #include <sys/systm.h>
50 #else
51 #include "myfbdevice.h"
52 #endif
53 
54 #include <dev/rcons/rcons.h>
55 #include <dev/wscons/wsdisplayvar.h>
56 
57 extern void rcons_bell(struct rconsole *);
58 
59 #if 0
60 #define RCONS_ISPRINT(c) ((((c) >= ' ') && ((c) <= '~')) || ((c) > 160))
61 #else
62 #define RCONS_ISPRINT(c) (((((c) >= ' ') && ((c) <= '~'))) || ((c) > 127))
63 #endif
64 #define RCONS_ISDIGIT(c) ((c) >= '0' && (c) <= '9')
65 
66 /* Initialize our operations set */
67 void
68 rcons_init_ops(rc)
69 	struct rconsole *rc;
70 {
71 	long tmp;
72 	int i, m;
73 
74 	m = sizeof(rc->rc_charmap) / sizeof(rc->rc_charmap[0]);
75 
76 	for (i = 0; i < m; i++)
77 		rc->rc_ops->mapchar(rc->rc_cookie, i, rc->rc_charmap + i);
78 
79 	/* Determine which attributes the device supports. */
80 #ifdef RASTERCONSOLE_FGCOL
81 	rc->rc_deffgcolor = RASTERCONSOLE_FGCOL;
82 #endif
83 #ifdef RASTERCONSOLE_BGCOL
84 	rc->rc_defbgcolor = RASTERCONSOLE_BGCOL;
85 #endif
86 	rc->rc_fgcolor = rc->rc_deffgcolor;
87 	rc->rc_bgcolor = rc->rc_defbgcolor;
88 	rc->rc_supwsflg = 0;
89 
90 	for (i = 1; i < 256; i <<= 1)
91 		if (rc->rc_ops->allocattr(rc->rc_cookie, 0, 0, i, &tmp) == 0)
92 			rc->rc_supwsflg |= i;
93 
94 	/* Allocate kernel output attribute */
95 	rc->rc_wsflg = WSATTR_HILIT;
96 	rcons_setcolor(rc, rc->rc_deffgcolor, rc->rc_defbgcolor);
97 	rc->rc_kern_attr = rc->rc_attr;
98 
99 	rc->rc_wsflg = 0;
100 	rcons_setcolor(rc, rc->rc_deffgcolor, rc->rc_defbgcolor);
101 	rc->rc_defattr = rc->rc_attr;
102 }
103 
104 /* Output (or at least handle) a string sent to the console */
105 void
106 rcons_puts(rc, str, n)
107 	struct rconsole *rc;
108 	const unsigned char *str;
109  	int n;
110 {
111 	int c, i, j;
112 	const unsigned char *cp;
113 
114 	/* Jump scroll */
115 	/* XXX maybe this should be an option? */
116 	if ((rc->rc_bits & FB_INESC) == 0) {
117 		/* Count newlines up to an escape sequence */
118 		i = 0;
119 		j = 0;
120 		for (cp = str; j++ < n && *cp != '\033'; ++cp) {
121 			if (*cp == '\n')
122 				++i;
123 			else if (*cp == '\013')
124 				--i;
125 		}
126 
127 		/* Only jump scroll two or more rows */
128 		if (rc->rc_row + i > rc->rc_maxrow + 1) {
129 			/* Erase the cursor (if necessary) */
130 			if (rc->rc_bits & FB_CURSOR)
131 				rcons_cursor(rc);
132 
133 			rcons_scroll(rc, i);
134 		}
135 	}
136 
137 	/* Process characters */
138 	while (--n >= 0) {
139 		c = *str;
140 		if (c == '\033') {
141 			/* Start an escape (perhaps aborting one in progress) */
142 			rc->rc_bits |= FB_INESC | FB_P0_DEFAULT | FB_P1_DEFAULT;
143 			rc->rc_bits &= ~(FB_P0 | FB_P1);
144 
145 			/* Most parameters default to 1 */
146 			rc->rc_p0 = rc->rc_p1 = 1;
147 		} else if (rc->rc_bits & FB_INESC) {
148 			rcons_esc(rc, c);
149 		} else {
150 			/* Erase the cursor (if necessary) */
151 			if (rc->rc_bits & FB_CURSOR)
152 				rcons_cursor(rc);
153 
154 			/* Display the character */
155 			if (RCONS_ISPRINT(c)) {
156 				/* Try to output as much as possible */
157 				j = rc->rc_maxcol - rc->rc_col;
158 				if (j > n)
159 					j = n;
160 				for (i = 1; i < j && RCONS_ISPRINT(str[i]); ++i)
161 					continue;
162 				rcons_text(rc, str, i);
163 				--i;
164 				str += i;
165 				n -= i;
166 			} else
167 				rcons_pctrl(rc, c);
168 		}
169 		++str;
170 	}
171 	/* Redraw the cursor (if necessary) */
172 	if ((rc->rc_bits & FB_CURSOR) == 0)
173 		rcons_cursor(rc);
174 }
175 
176 
177 /* Handle a control character sent to the console */
178 void
179 rcons_pctrl(rc, c)
180 	struct rconsole *rc;
181 	int c;
182 {
183 
184 	switch (c) {
185 	case '\r':	/* Carriage return */
186 		rc->rc_col = 0;
187 		break;
188 
189 	case '\b':	/* Backspace */
190 		if (rc->rc_col > 0)
191 			(rc->rc_col)--;
192 		break;
193 
194 	case '\v':	/* Vertical tab */
195 		if (rc->rc_row > 0)
196 			(rc->rc_row)--;
197 		break;
198 
199 	case '\f':	/* Formfeed */
200 		rc->rc_row = rc->rc_col = 0;
201 		rcons_clear2eop(rc);
202 		break;
203 
204 	case '\n':	/* Linefeed */
205 		(rc->rc_row)++;
206 		if (rc->rc_row >= rc->rc_maxrow)
207 			rcons_scroll(rc, 1);
208 		break;
209 
210 	case '\a':	/* Bell */
211 		rcons_bell(rc);
212 		break;
213 
214 	case '\t':	/* Horizontal tab */
215 		rc->rc_col = (rc->rc_col + 8) & ~7;
216 		if (rc->rc_col >= rc->rc_maxcol)
217 			rc->rc_col = rc->rc_maxcol;
218 		break;
219 	}
220 }
221 
222 /* Handle the next character in an escape sequence */
223 void
224 rcons_esc(rc, c)
225 	struct rconsole *rc;
226 	int c;
227 {
228 
229 	if (c == '[') {
230 		/* Parameter 0 */
231 		rc->rc_bits &= ~FB_P1;
232 		rc->rc_bits |= FB_P0;
233 	} else if (c == ';') {
234 		/* Parameter 1 */
235 		rc->rc_bits &= ~FB_P0;
236 		rc->rc_bits |= FB_P1;
237 	} else if (RCONS_ISDIGIT(c)) {
238 		/* Add a digit to a parameter */
239 		if (rc->rc_bits & FB_P0) {
240 			/* Parameter 0 */
241 			if (rc->rc_bits & FB_P0_DEFAULT) {
242 				rc->rc_bits &= ~FB_P0_DEFAULT;
243 				rc->rc_p0 = 0;
244 			}
245 			rc->rc_p0 *= 10;
246 			rc->rc_p0 += c - '0';
247 		} else if (rc->rc_bits & FB_P1) {
248 			/* Parameter 1 */
249 			if (rc->rc_bits & FB_P1_DEFAULT) {
250 				rc->rc_bits &= ~FB_P1_DEFAULT;
251 				rc->rc_p1 = 0;
252 			}
253 			rc->rc_p1 *= 10;
254 			rc->rc_p1 += c - '0';
255 		}
256 	} else {
257 		/* Erase the cursor (if necessary) */
258 		if (rc->rc_bits & FB_CURSOR)
259 			rcons_cursor(rc);
260 
261 		/* Process the completed escape sequence */
262 		rcons_doesc(rc, c);
263 		rc->rc_bits &= ~FB_INESC;
264 	}
265 }
266 
267 
268 /* Handle an SGR (Select Graphic Rendition) escape */
269 void
270 rcons_sgresc(rc, c)
271 	struct rconsole *rc;
272 	int c;
273 {
274 
275 	switch (c) {
276 	/* Clear all attributes || End underline */
277 	case 0:
278 		rc->rc_wsflg = 0;
279 		rc->rc_fgcolor = rc->rc_deffgcolor;
280 		rc->rc_bgcolor = rc->rc_defbgcolor;
281 		rc->rc_attr = rc->rc_defattr;
282 		break;
283 
284 	/* ANSI foreground color */
285 	case 30: case 31: case 32: case 33:
286 	case 34: case 35: case 36: case 37:
287 		rcons_setcolor(rc, c - 30, rc->rc_bgcolor);
288 		break;
289 
290 	/* ANSI background color */
291 	case 40: case 41: case 42: case 43:
292 	case 44: case 45: case 46: case 47:
293 		rcons_setcolor(rc, rc->rc_fgcolor, c - 40);
294 		break;
295 
296 	/* Begin reverse */
297 	case 7:
298 		rc->rc_wsflg |= WSATTR_REVERSE;
299 		rcons_setcolor(rc, rc->rc_fgcolor, rc->rc_bgcolor);
300 		break;
301 
302 	/* Begin bold */
303 	case 1:
304 		rc->rc_wsflg |= WSATTR_HILIT;
305 		rcons_setcolor(rc, rc->rc_fgcolor, rc->rc_bgcolor);
306 		break;
307 
308 	/* Begin underline */
309 	case 4:
310 		rc->rc_wsflg |= WSATTR_UNDERLINE;
311 		rcons_setcolor(rc, rc->rc_fgcolor, rc->rc_bgcolor);
312 		break;
313 	}
314 }
315 
316 
317 /* Process a complete escape sequence */
318 void
319 rcons_doesc(rc, c)
320 	struct rconsole *rc;
321 	int c;
322 {
323 
324 #ifdef notdef
325 	/* XXX add escape sequence to enable visual (and audible) bell */
326 	rc->rc_bits = FB_VISBELL;
327 #endif
328 
329 	switch (c) {
330 
331 	case '@':
332 		/* Insert Character (ICH) */
333 		rcons_insertchar(rc, rc->rc_p0);
334 		break;
335 
336 	case 'A':
337 		/* Cursor Up (CUU) */
338 		rc->rc_row -= rc->rc_p0;
339 		if (rc->rc_row < 0)
340 			rc->rc_row = 0;
341 		break;
342 
343 	case 'B':
344 		/* Cursor Down (CUD) */
345 		rc->rc_row += rc->rc_p0;
346 		if (rc->rc_row >= rc->rc_maxrow)
347 			rc->rc_row = rc->rc_maxrow - 1;
348 		break;
349 
350 	case 'C':
351 		/* Cursor Forward (CUF) */
352 		rc->rc_col += rc->rc_p0;
353 		if (rc->rc_col >= rc->rc_maxcol)
354 			rc->rc_col = rc->rc_maxcol - 1;
355 		break;
356 
357 	case 'D':
358 		/* Cursor Backward (CUB) */
359 		rc->rc_col -= rc->rc_p0;
360 		if (rc->rc_col < 0)
361 			rc->rc_col = 0;
362 		break;
363 
364 	case 'E':
365 		/* Cursor Next Line (CNL) */
366 		rc->rc_col = 0;
367 		rc->rc_row += rc->rc_p0;
368 		if (rc->rc_row >= rc->rc_maxrow)
369 			rc->rc_row = rc->rc_maxrow - 1;
370 		break;
371 
372 	case 'f':
373 		/* Horizontal And Vertical Position (HVP) */
374 	case 'H':
375 		/* Cursor Position (CUP) */
376 		rc->rc_col = rc->rc_p1 - 1;
377 		if (rc->rc_col < 0)
378 			rc->rc_col = 0;
379 		else if (rc->rc_col >= rc->rc_maxcol)
380 			rc->rc_col = rc->rc_maxcol - 1;
381 
382 		rc->rc_row = rc->rc_p0 - 1;
383 		if (rc->rc_row < 0)
384 			rc->rc_row = 0;
385 		else if (rc->rc_row >= rc->rc_maxrow)
386 			rc->rc_row = rc->rc_maxrow - 1;
387 		break;
388 
389 	case 'J':
390 		/* Erase in Display (ED) */
391 		rcons_clear2eop(rc);
392 		break;
393 
394 	case 'K':
395 		/* Erase in Line (EL) */
396 		rcons_clear2eol(rc);
397 		break;
398 
399 	case 'L':
400 		/* Insert Line (IL) */
401 		rcons_insertline(rc, rc->rc_p0);
402 		break;
403 
404 	case 'M':
405 		/* Delete Line (DL) */
406 		rcons_delline(rc, rc->rc_p0);
407 		break;
408 
409 	case 'P':
410 		/* Delete Character (DCH) */
411 		rcons_delchar(rc, rc->rc_p0);
412 		break;
413 
414 	case 'm':
415 		/* Select Graphic Rendition (SGR) */
416 		/* (defaults to zero) */
417 		if (rc->rc_bits & FB_P0_DEFAULT)
418 			rc->rc_p0 = 0;
419 
420 		if (rc->rc_bits & FB_P1_DEFAULT)
421 			rc->rc_p1 = 0;
422 
423 		rcons_sgresc(rc, rc->rc_p0);
424 
425 		if (rc->rc_bits & FB_P1)
426 			rcons_sgresc(rc, rc->rc_p1);
427 
428 		break;
429 
430 	/*
431 	 * XXX: setting SUNBOW and SUNWOB should probably affect
432 	 * deffgcolor, defbgcolor and defattr too.
433 	 */
434 	case 'p':
435 		/* Black On White (SUNBOW) */
436 		rcons_setcolor(rc, WSCOL_BLACK, WSCOL_WHITE);
437 		break;
438 
439 	case 'q':
440 		/* White On Black (SUNWOB) */
441 		rcons_setcolor(rc, WSCOL_WHITE, WSCOL_BLACK);
442 		break;
443 
444 	case 'r':
445 		/* Set scrolling (SUNSCRL) */
446 		/* (defaults to zero) */
447 		if (rc->rc_bits & FB_P0_DEFAULT)
448 			rc->rc_p0 = 0;
449 		/* XXX not implemented yet */
450 		rc->rc_scroll = rc->rc_p0;
451 		break;
452 
453 	case 's':
454 		/* Reset terminal emulator (SUNRESET) */
455 		rc->rc_wsflg = 0;
456 		rc->rc_scroll = 0;
457 		rc->rc_bits &= ~FB_NO_CURSOR;
458 		rc->rc_fgcolor = rc->rc_deffgcolor;
459 		rc->rc_bgcolor = rc->rc_defbgcolor;
460 		rc->rc_attr = rc->rc_defattr;
461 
462 		if (rc->rc_bits & FB_INVERT)
463 			rcons_invert(rc, 0);
464 		break;
465 #ifdef notyet
466 	/*
467 	 * XXX following two read \E[?25h and \E[?25l. rcons
468 	 * can't currently handle the '?'.
469 	 */
470 	case 'h':
471 		/* Normal/very visible cursor */
472 		if (rc->rc_p0 == 25) {
473 			rc->rc_bits &= ~FB_NO_CURSOR;
474 
475 			if (rc->rc_bits & FB_CURSOR) {
476 				rc->rc_bits ^= FB_CURSOR;
477 				rcons_cursor(rc);
478 			}
479 		}
480 		break;
481 
482 	case 'l':
483 		/* Invisible cursor */
484 		if (rc->rc_p0 == 25 && (rc->rc_bits & FB_NO_CURSOR) == 0) {
485 			if (rc->rc_bits & FB_CURSOR)
486 				rcons_cursor(rc);
487 
488 			rc->rc_bits |= FB_NO_CURSOR;
489 		}
490 		break;
491 #endif
492 	}
493 }
494 
495 /* Set ANSI colors */
496 void
497 rcons_setcolor(rc, fg, bg)
498 	struct rconsole *rc;
499 	int fg, bg;
500 {
501 	int flg;
502 
503 	if (fg > WSCOL_WHITE || fg < 0)
504 		return;
505 
506 	if (bg > WSCOL_WHITE || bg < 0)
507 		return;
508 
509 #ifdef RASTERCONS_WONB
510 	flg = bg;
511 	bg = fg;
512 	fg = flg;
513 #endif
514 
515 	/* Emulate WSATTR_REVERSE attribute if it's not supported */
516 	if ((rc->rc_wsflg & WSATTR_REVERSE) &&
517 	    !(rc->rc_supwsflg & WSATTR_REVERSE)) {
518 		flg = bg;
519 		bg = fg;
520 		fg = flg;
521 	}
522 
523 	/*
524 	 * Mask out unsupported flags and get attribute
525 	 * XXX - always ask for WSCOLORS if supported (why shouldn't we?)
526 	 */
527 	flg = (rc->rc_wsflg | WSATTR_WSCOLORS) & rc->rc_supwsflg;
528 	rc->rc_bgcolor = bg;
529 	rc->rc_fgcolor = fg;
530 	rc->rc_ops->allocattr(rc->rc_cookie, fg, bg, flg, &rc->rc_attr);
531 }
532 
533 
534 /* Actually write a string to the frame buffer */
535 void
536 rcons_text(rc, str, n)
537 	struct rconsole *rc;
538 	const unsigned char *str;
539 	int n;
540 {
541 	u_int uc;
542 
543 	while (n--) {
544 		uc = rc->rc_charmap[*str++ & 255];
545 		rc->rc_ops->putchar(rc->rc_cookie, rc->rc_row, rc->rc_col++,
546 		    uc, rc->rc_attr);
547 	}
548 
549 	if (rc->rc_col >= rc->rc_maxcol) {
550 		rc->rc_col = 0;
551 		rc->rc_row++;
552 	}
553 
554 	if (rc->rc_row >= rc->rc_maxrow)
555 		rcons_scroll(rc, 1);
556 }
557 
558 /* Paint (or unpaint) the cursor */
559 void
560 rcons_cursor(rc)
561 	struct rconsole *rc;
562 {
563 	rc->rc_bits ^= FB_CURSOR;
564 
565 	if (rc->rc_bits & FB_NO_CURSOR)
566 		return;
567 
568 	rc->rc_ops->cursor(rc->rc_cookie, rc->rc_bits & FB_CURSOR,
569 	    rc->rc_row, rc->rc_col);
570 }
571 
572 /* Possibly change to SUNWOB or SUNBOW mode */
573 void
574 rcons_invert(rc, wob)
575 	struct rconsole *rc;
576 	int wob;
577 {
578 
579 	rc->rc_bits ^= FB_INVERT;
580 	/* XXX how do we do we invert the framebuffer?? */
581 }
582 
583 /* Clear to the end of the page */
584 void
585 rcons_clear2eop(rc)
586 	struct rconsole *rc;
587 {
588 	if (rc->rc_col || rc->rc_row) {
589 		rcons_clear2eol(rc);
590 
591 		if (rc->rc_row < (rc->rc_maxrow - 1))
592 			rc->rc_ops->eraserows(rc->rc_cookie, rc->rc_row + 1,
593 			    rc->rc_maxrow, rc->rc_attr);
594 	} else
595 		rc->rc_ops->eraserows(rc->rc_cookie, 0, rc->rc_maxrow,
596 		    rc->rc_attr);
597 }
598 
599 /* Clear to the end of the line */
600 void
601 rcons_clear2eol(rc)
602 	struct rconsole *rc;
603 {
604 	rc->rc_ops->erasecols(rc->rc_cookie, rc->rc_row, rc->rc_col,
605 	    rc->rc_maxcol - rc->rc_col, rc->rc_attr);
606 }
607 
608 
609 /* Scroll up */
610 void
611 rcons_scroll(rc, n)
612 	struct rconsole *rc;
613 	int n;
614 {
615 	/* Can't scroll more than the whole screen */
616 	if (n > rc->rc_maxrow)
617 		n = rc->rc_maxrow;
618 
619 	/* Calculate new row */
620 	rc->rc_row -= n;
621 
622 	if (rc->rc_row < 0)
623 		rc->rc_row = 0;
624 
625 	rc->rc_ops->copyrows(rc->rc_cookie, n, 0, rc->rc_maxrow - n);
626 	rc->rc_ops->eraserows(rc->rc_cookie, rc->rc_maxrow - n, n,  rc->rc_attr);
627 }
628 
629 /* Delete characters */
630 void
631 rcons_delchar(rc, n)
632 	struct rconsole *rc;
633 	int n;
634 {
635 	/* Can't delete more chars than there are */
636 	if (n > rc->rc_maxcol - rc->rc_col)
637 		n = rc->rc_maxcol - rc->rc_col;
638 
639 	rc->rc_ops->copycols(rc->rc_cookie, rc->rc_row, rc->rc_col + n,
640 	    rc->rc_col, rc->rc_maxcol - rc->rc_col - n);
641 
642 	rc->rc_ops->erasecols(rc->rc_cookie, rc->rc_row,
643 	    rc->rc_maxcol - n, n, rc->rc_attr);
644 }
645 
646 /* Delete a number of lines */
647 void
648 rcons_delline(rc, n)
649 	struct rconsole *rc;
650 	int n;
651 {
652 	/* Can't delete more lines than there are */
653 	if (n > rc->rc_maxrow - rc->rc_row)
654 		n = rc->rc_maxrow - rc->rc_row;
655 
656 	rc->rc_ops->copyrows(rc->rc_cookie, rc->rc_row + n, rc->rc_row,
657 	    rc->rc_maxrow - rc->rc_row - n);
658 
659 	rc->rc_ops->eraserows(rc->rc_cookie, rc->rc_maxrow - n, n,
660 	    rc->rc_attr);
661 }
662 
663 /* Insert some characters */
664 void
665 rcons_insertchar(rc, n)
666 	struct rconsole *rc;
667 	int n;
668 {
669 	/* Can't insert more chars than can fit */
670 	if (n > rc->rc_maxcol - rc->rc_col)
671 		n = rc->rc_maxcol - rc->rc_col - 1;
672 
673 	rc->rc_ops->copycols(rc->rc_cookie, rc->rc_row, rc->rc_col,
674 	    rc->rc_col + n, rc->rc_maxcol - rc->rc_col - n - 1);
675 
676 	rc->rc_ops->erasecols(rc->rc_cookie, rc->rc_row, rc->rc_col,
677 	    n, rc->rc_attr);
678 }
679 
680 /* Insert some lines */
681 void
682 rcons_insertline(rc, n)
683 	struct rconsole *rc;
684 	int n;
685 {
686 	/* Can't insert more lines than can fit */
687 	if (n > rc->rc_maxrow - rc->rc_row)
688 		n = rc->rc_maxrow - rc->rc_row;
689 
690 	rc->rc_ops->copyrows(rc->rc_cookie, rc->rc_row, rc->rc_row + n,
691 	    rc->rc_maxrow - rc->rc_row - n);
692 
693 	rc->rc_ops->eraserows(rc->rc_cookie, rc->rc_row, n,
694 	    rc->rc_attr);
695 }
696 
697 /* end of rcons_subr.c */
698