1 /*        $NetBSD: rcons_subr.c,v 1.18 2009/03/14 21:04:22 dsl 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.18 2009/03/14 21:04:22 dsl 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
rcons_init_ops(struct rconsole * rc)68 rcons_init_ops(struct rconsole *rc)
69 {
70           long tmp;
71           int i, m;
72 
73           m = sizeof(rc->rc_charmap) / sizeof(rc->rc_charmap[0]);
74 
75           for (i = 0; i < m; i++)
76                     rc->rc_ops->mapchar(rc->rc_cookie, i, rc->rc_charmap + i);
77 
78           /* Determine which attributes the device supports. */
79 #ifdef RASTERCONSOLE_FGCOL
80           rc->rc_deffgcolor = RASTERCONSOLE_FGCOL;
81 #endif
82 #ifdef RASTERCONSOLE_BGCOL
83           rc->rc_defbgcolor = RASTERCONSOLE_BGCOL;
84 #endif
85           rc->rc_fgcolor = rc->rc_deffgcolor;
86           rc->rc_bgcolor = rc->rc_defbgcolor;
87           rc->rc_supwsflg = 0;
88 
89           for (i = 1; i < 256; i <<= 1)
90                     if (rc->rc_ops->allocattr(rc->rc_cookie, 0, 0, i, &tmp) == 0)
91                               rc->rc_supwsflg |= i;
92 
93           /* Allocate kernel output attribute */
94           rc->rc_wsflg = WSATTR_HILIT;
95           rcons_setcolor(rc, rc->rc_deffgcolor, rc->rc_defbgcolor);
96           rc->rc_kern_attr = rc->rc_attr;
97 
98           rc->rc_wsflg = 0;
99           rcons_setcolor(rc, rc->rc_deffgcolor, rc->rc_defbgcolor);
100           rc->rc_defattr = rc->rc_attr;
101 }
102 
103 /* Output (or at least handle) a string sent to the console */
104 void
rcons_puts(struct rconsole * rc,const unsigned char * str,int n)105 rcons_puts(struct rconsole *rc, const unsigned char *str, int n)
106 {
107           int c, i, j;
108           const unsigned char *cp;
109 
110           /* Jump scroll */
111           /* XXX maybe this should be an option? */
112           if ((rc->rc_bits & FB_INESC) == 0) {
113                     /* Count newlines up to an escape sequence */
114                     i = 0;
115                     j = 0;
116                     for (cp = str; j++ < n && *cp != '\033'; ++cp) {
117                               if (*cp == '\n')
118                                         ++i;
119                               else if (*cp == '\013')
120                                         --i;
121                     }
122 
123                     /* Only jump scroll two or more rows */
124                     if (rc->rc_row + i > rc->rc_maxrow + 1) {
125                               /* Erase the cursor (if necessary) */
126                               if (rc->rc_bits & FB_CURSOR)
127                                         rcons_cursor(rc);
128 
129                               rcons_scroll(rc, i);
130                     }
131           }
132 
133           /* Process characters */
134           while (--n >= 0) {
135                     c = *str;
136                     if (c == '\033') {
137                               /* Start an escape (perhaps aborting one in progress) */
138                               rc->rc_bits |= FB_INESC | FB_P0_DEFAULT | FB_P1_DEFAULT;
139                               rc->rc_bits &= ~(FB_P0 | FB_P1);
140 
141                               /* Most parameters default to 1 */
142                               rc->rc_p0 = rc->rc_p1 = 1;
143                     } else if (rc->rc_bits & FB_INESC) {
144                               rcons_esc(rc, c);
145                     } else {
146                               /* Erase the cursor (if necessary) */
147                               if (rc->rc_bits & FB_CURSOR)
148                                         rcons_cursor(rc);
149 
150                               /* Display the character */
151                               if (RCONS_ISPRINT(c)) {
152                                         /* Try to output as much as possible */
153                                         j = rc->rc_maxcol - rc->rc_col;
154                                         if (j > n)
155                                                   j = n;
156                                         for (i = 1; i < j && RCONS_ISPRINT(str[i]); ++i)
157                                                   continue;
158                                         rcons_text(rc, str, i);
159                                         --i;
160                                         str += i;
161                                         n -= i;
162                               } else
163                                         rcons_pctrl(rc, c);
164                     }
165                     ++str;
166           }
167           /* Redraw the cursor (if necessary) */
168           if ((rc->rc_bits & FB_CURSOR) == 0)
169                     rcons_cursor(rc);
170 }
171 
172 
173 /* Handle a control character sent to the console */
174 void
rcons_pctrl(struct rconsole * rc,int c)175 rcons_pctrl(struct rconsole *rc, int c)
176 {
177 
178           switch (c) {
179           case '\r':          /* Carriage return */
180                     rc->rc_col = 0;
181                     break;
182 
183           case '\b':          /* Backspace */
184                     if (rc->rc_col > 0)
185                               (rc->rc_col)--;
186                     break;
187 
188           case '\v':          /* Vertical tab */
189                     if (rc->rc_row > 0)
190                               (rc->rc_row)--;
191                     break;
192 
193           case '\f':          /* Formfeed */
194                     rc->rc_row = rc->rc_col = 0;
195                     rcons_clear2eop(rc);
196                     break;
197 
198           case '\n':          /* Linefeed */
199                     (rc->rc_row)++;
200                     if (rc->rc_row >= rc->rc_maxrow)
201                               rcons_scroll(rc, 1);
202                     break;
203 
204           case '\a':          /* Bell */
205                     rcons_bell(rc);
206                     break;
207 
208           case '\t':          /* Horizontal tab */
209                     rc->rc_col = (rc->rc_col + 8) & ~7;
210                     if (rc->rc_col >= rc->rc_maxcol)
211                               rc->rc_col = rc->rc_maxcol;
212                     break;
213           }
214 }
215 
216 /* Handle the next character in an escape sequence */
217 void
rcons_esc(struct rconsole * rc,int c)218 rcons_esc(struct rconsole *rc, int c)
219 {
220 
221           if (c == '[') {
222                     /* Parameter 0 */
223                     rc->rc_bits &= ~FB_P1;
224                     rc->rc_bits |= FB_P0;
225           } else if (c == ';') {
226                     /* Parameter 1 */
227                     rc->rc_bits &= ~FB_P0;
228                     rc->rc_bits |= FB_P1;
229           } else if (RCONS_ISDIGIT(c)) {
230                     /* Add a digit to a parameter */
231                     if (rc->rc_bits & FB_P0) {
232                               /* Parameter 0 */
233                               if (rc->rc_bits & FB_P0_DEFAULT) {
234                                         rc->rc_bits &= ~FB_P0_DEFAULT;
235                                         rc->rc_p0 = 0;
236                               }
237                               rc->rc_p0 *= 10;
238                               rc->rc_p0 += c - '0';
239                     } else if (rc->rc_bits & FB_P1) {
240                               /* Parameter 1 */
241                               if (rc->rc_bits & FB_P1_DEFAULT) {
242                                         rc->rc_bits &= ~FB_P1_DEFAULT;
243                                         rc->rc_p1 = 0;
244                               }
245                               rc->rc_p1 *= 10;
246                               rc->rc_p1 += c - '0';
247                     }
248           } else {
249                     /* Erase the cursor (if necessary) */
250                     if (rc->rc_bits & FB_CURSOR)
251                               rcons_cursor(rc);
252 
253                     /* Process the completed escape sequence */
254                     rcons_doesc(rc, c);
255                     rc->rc_bits &= ~FB_INESC;
256           }
257 }
258 
259 
260 /* Handle an SGR (Select Graphic Rendition) escape */
261 void
rcons_sgresc(struct rconsole * rc,int c)262 rcons_sgresc(struct rconsole *rc, int c)
263 {
264 
265           switch (c) {
266           /* Clear all attributes || End underline */
267           case 0:
268                     rc->rc_wsflg = 0;
269                     rc->rc_fgcolor = rc->rc_deffgcolor;
270                     rc->rc_bgcolor = rc->rc_defbgcolor;
271                     rc->rc_attr = rc->rc_defattr;
272                     break;
273 
274           /* ANSI foreground color */
275           case 30: case 31: case 32: case 33:
276           case 34: case 35: case 36: case 37:
277                     rcons_setcolor(rc, c - 30, rc->rc_bgcolor);
278                     break;
279 
280           /* ANSI background color */
281           case 40: case 41: case 42: case 43:
282           case 44: case 45: case 46: case 47:
283                     rcons_setcolor(rc, rc->rc_fgcolor, c - 40);
284                     break;
285 
286           /* Begin reverse */
287           case 7:
288                     rc->rc_wsflg |= WSATTR_REVERSE;
289                     rcons_setcolor(rc, rc->rc_fgcolor, rc->rc_bgcolor);
290                     break;
291 
292           /* Begin bold */
293           case 1:
294                     rc->rc_wsflg |= WSATTR_HILIT;
295                     rcons_setcolor(rc, rc->rc_fgcolor, rc->rc_bgcolor);
296                     break;
297 
298           /* Begin underline */
299           case 4:
300                     rc->rc_wsflg |= WSATTR_UNDERLINE;
301                     rcons_setcolor(rc, rc->rc_fgcolor, rc->rc_bgcolor);
302                     break;
303           }
304 }
305 
306 
307 /* Process a complete escape sequence */
308 void
rcons_doesc(struct rconsole * rc,int c)309 rcons_doesc(struct rconsole *rc, int c)
310 {
311 
312 #ifdef notdef
313           /* XXX add escape sequence to enable visual (and audible) bell */
314           rc->rc_bits = FB_VISBELL;
315 #endif
316 
317           switch (c) {
318 
319           case '@':
320                     /* Insert Character (ICH) */
321                     rcons_insertchar(rc, rc->rc_p0);
322                     break;
323 
324           case 'A':
325                     /* Cursor Up (CUU) */
326                     if (rc->rc_row >= rc->rc_p0)
327                               rc->rc_row -= rc->rc_p0;
328                     else
329                               rc->rc_row = 0;
330                     break;
331 
332           case 'B':
333                     /* Cursor Down (CUD) */
334                     rc->rc_row += rc->rc_p0;
335                     if (rc->rc_row >= rc->rc_maxrow)
336                               rc->rc_row = rc->rc_maxrow - 1;
337                     break;
338 
339           case 'C':
340                     /* Cursor Forward (CUF) */
341                     rc->rc_col += rc->rc_p0;
342                     if (rc->rc_col >= rc->rc_maxcol)
343                               rc->rc_col = rc->rc_maxcol - 1;
344                     break;
345 
346           case 'D':
347                     /* Cursor Backward (CUB) */
348                     if (rc->rc_col >= rc->rc_p0)
349                               rc->rc_col -= rc->rc_p0;
350                     else
351                               rc->rc_col = 0;
352                     break;
353 
354           case 'E':
355                     /* Cursor Next Line (CNL) */
356                     rc->rc_col = 0;
357                     rc->rc_row += rc->rc_p0;
358                     if (rc->rc_row >= rc->rc_maxrow)
359                               rc->rc_row = rc->rc_maxrow - 1;
360                     break;
361 
362           case 'f':
363                     /* Horizontal And Vertical Position (HVP) */
364           case 'H':
365                     /* Cursor Position (CUP) */
366                     rc->rc_col = MIN(MAX(rc->rc_p1, 1), rc->rc_maxcol) - 1;
367                     rc->rc_row = MIN(MAX(rc->rc_p0, 1), rc->rc_maxrow) - 1;
368                     break;
369 
370           case 'J':
371                     /* Erase in Display (ED) */
372                     rcons_clear2eop(rc);
373                     break;
374 
375           case 'K':
376                     /* Erase in Line (EL) */
377                     rcons_clear2eol(rc);
378                     break;
379 
380           case 'L':
381                     /* Insert Line (IL) */
382                     rcons_insertline(rc, rc->rc_p0);
383                     break;
384 
385           case 'M':
386                     /* Delete Line (DL) */
387                     rcons_delline(rc, rc->rc_p0);
388                     break;
389 
390           case 'P':
391                     /* Delete Character (DCH) */
392                     rcons_delchar(rc, rc->rc_p0);
393                     break;
394 
395           case 'm':
396                     /* Select Graphic Rendition (SGR) */
397                     /* (defaults to zero) */
398                     if (rc->rc_bits & FB_P0_DEFAULT)
399                               rc->rc_p0 = 0;
400 
401                     if (rc->rc_bits & FB_P1_DEFAULT)
402                               rc->rc_p1 = 0;
403 
404                     rcons_sgresc(rc, rc->rc_p0);
405 
406                     if (rc->rc_bits & FB_P1)
407                               rcons_sgresc(rc, rc->rc_p1);
408 
409                     break;
410 
411           /*
412            * XXX: setting SUNBOW and SUNWOB should probably affect
413            * deffgcolor, defbgcolor and defattr too.
414            */
415           case 'p':
416                     /* Black On White (SUNBOW) */
417                     rcons_setcolor(rc, WSCOL_BLACK, WSCOL_WHITE);
418                     break;
419 
420           case 'q':
421                     /* White On Black (SUNWOB) */
422                     rcons_setcolor(rc, WSCOL_WHITE, WSCOL_BLACK);
423                     break;
424 
425           case 'r':
426                     /* Set scrolling (SUNSCRL) */
427                     /* (defaults to zero) */
428                     if (rc->rc_bits & FB_P0_DEFAULT)
429                               rc->rc_p0 = 0;
430                     /* XXX not implemented yet */
431                     rc->rc_scroll = rc->rc_p0;
432                     break;
433 
434           case 's':
435                     /* Reset terminal emulator (SUNRESET) */
436                     rc->rc_wsflg = 0;
437                     rc->rc_scroll = 0;
438                     rc->rc_bits &= ~FB_NO_CURSOR;
439                     rc->rc_fgcolor = rc->rc_deffgcolor;
440                     rc->rc_bgcolor = rc->rc_defbgcolor;
441                     rc->rc_attr = rc->rc_defattr;
442 
443                     if (rc->rc_bits & FB_INVERT)
444                               rcons_invert(rc, 0);
445                     break;
446 #ifdef notyet
447           /*
448            * XXX following two read \E[?25h and \E[?25l. rcons
449            * can't currently handle the '?'.
450            */
451           case 'h':
452                     /* Normal/very visible cursor */
453                     if (rc->rc_p0 == 25) {
454                               rc->rc_bits &= ~FB_NO_CURSOR;
455 
456                               if (rc->rc_bits & FB_CURSOR) {
457                                         rc->rc_bits ^= FB_CURSOR;
458                                         rcons_cursor(rc);
459                               }
460                     }
461                     break;
462 
463           case 'l':
464                     /* Invisible cursor */
465                     if (rc->rc_p0 == 25 && (rc->rc_bits & FB_NO_CURSOR) == 0) {
466                               if (rc->rc_bits & FB_CURSOR)
467                                         rcons_cursor(rc);
468 
469                               rc->rc_bits |= FB_NO_CURSOR;
470                     }
471                     break;
472 #endif
473           }
474 }
475 
476 /* Set ANSI colors */
477 void
rcons_setcolor(struct rconsole * rc,int fg,int bg)478 rcons_setcolor(struct rconsole *rc, int fg, int bg)
479 {
480           int flg;
481 
482           if (fg > WSCOL_WHITE || fg < 0)
483                     return;
484 
485           if (bg > WSCOL_WHITE || bg < 0)
486                     return;
487 
488 #ifdef RASTERCONS_WONB
489           flg = bg;
490           bg = fg;
491           fg = flg;
492 #endif
493 
494           /* Emulate WSATTR_REVERSE attribute if it's not supported */
495           if ((rc->rc_wsflg & WSATTR_REVERSE) &&
496               !(rc->rc_supwsflg & WSATTR_REVERSE)) {
497                     flg = bg;
498                     bg = fg;
499                     fg = flg;
500           }
501 
502           /*
503            * Mask out unsupported flags and get attribute
504            * XXX - always ask for WSCOLORS if supported (why shouldn't we?)
505            */
506           flg = (rc->rc_wsflg | WSATTR_WSCOLORS) & rc->rc_supwsflg;
507           rc->rc_bgcolor = bg;
508           rc->rc_fgcolor = fg;
509           rc->rc_ops->allocattr(rc->rc_cookie, fg, bg, flg, &rc->rc_attr);
510 }
511 
512 
513 /* Actually write a string to the frame buffer */
514 void
rcons_text(struct rconsole * rc,const unsigned char * str,int n)515 rcons_text(struct rconsole *rc, const unsigned char *str, int n)
516 {
517           u_int uc;
518 
519           while (n--) {
520                     uc = rc->rc_charmap[*str++ & 255];
521                     rc->rc_ops->putchar(rc->rc_cookie, rc->rc_row, rc->rc_col++,
522                         uc, rc->rc_attr);
523           }
524 
525           if (rc->rc_col >= rc->rc_maxcol) {
526                     rc->rc_col = 0;
527                     rc->rc_row++;
528           }
529 
530           if (rc->rc_row >= rc->rc_maxrow)
531                     rcons_scroll(rc, 1);
532 }
533 
534 /* Paint (or unpaint) the cursor */
535 void
rcons_cursor(struct rconsole * rc)536 rcons_cursor(struct rconsole *rc)
537 {
538           rc->rc_bits ^= FB_CURSOR;
539 
540           if (rc->rc_bits & FB_NO_CURSOR)
541                     return;
542 
543           rc->rc_ops->cursor(rc->rc_cookie, rc->rc_bits & FB_CURSOR,
544               rc->rc_row, rc->rc_col);
545 }
546 
547 /* Possibly change to SUNWOB or SUNBOW mode */
548 void
rcons_invert(struct rconsole * rc,int wob)549 rcons_invert(struct rconsole *rc, int wob)
550 {
551 
552           rc->rc_bits ^= FB_INVERT;
553           /* XXX how do we do we invert the framebuffer?? */
554 }
555 
556 /* Clear to the end of the page */
557 void
rcons_clear2eop(struct rconsole * rc)558 rcons_clear2eop(struct rconsole *rc)
559 {
560           if (rc->rc_col || rc->rc_row) {
561                     rcons_clear2eol(rc);
562 
563                     if (rc->rc_row < (rc->rc_maxrow - 1))
564                               rc->rc_ops->eraserows(rc->rc_cookie, rc->rc_row + 1,
565                                   rc->rc_maxrow, rc->rc_attr);
566           } else
567                     rc->rc_ops->eraserows(rc->rc_cookie, 0, rc->rc_maxrow,
568                         rc->rc_attr);
569 }
570 
571 /* Clear to the end of the line */
572 void
rcons_clear2eol(struct rconsole * rc)573 rcons_clear2eol(struct rconsole *rc)
574 {
575           rc->rc_ops->erasecols(rc->rc_cookie, rc->rc_row, rc->rc_col,
576               rc->rc_maxcol - rc->rc_col, rc->rc_attr);
577 }
578 
579 
580 /* Scroll up */
581 void
rcons_scroll(struct rconsole * rc,int n)582 rcons_scroll(struct rconsole *rc, int n)
583 {
584           /* Can't scroll more than the whole screen */
585           if (n > rc->rc_maxrow)
586                     n = rc->rc_maxrow;
587 
588           /* Calculate new row */
589           if (rc->rc_row >= n)
590                     rc->rc_row -= n;
591           else
592                     rc->rc_row = 0;
593 
594           rc->rc_ops->copyrows(rc->rc_cookie, n, 0, rc->rc_maxrow - n);
595           rc->rc_ops->eraserows(rc->rc_cookie, rc->rc_maxrow - n, n,  rc->rc_attr);
596 }
597 
598 /* Delete characters */
599 void
rcons_delchar(struct rconsole * rc,int n)600 rcons_delchar(struct rconsole *rc, int n)
601 {
602           /* Can't delete more chars than there are */
603           if (n > rc->rc_maxcol - rc->rc_col)
604                     n = rc->rc_maxcol - rc->rc_col;
605 
606           rc->rc_ops->copycols(rc->rc_cookie, rc->rc_row, rc->rc_col + n,
607               rc->rc_col, rc->rc_maxcol - rc->rc_col - n);
608 
609           rc->rc_ops->erasecols(rc->rc_cookie, rc->rc_row,
610               rc->rc_maxcol - n, n, rc->rc_attr);
611 }
612 
613 /* Delete a number of lines */
614 void
rcons_delline(struct rconsole * rc,int n)615 rcons_delline(struct rconsole *rc, int n)
616 {
617           /* Can't delete more lines than there are */
618           if (n > rc->rc_maxrow - rc->rc_row)
619                     n = rc->rc_maxrow - rc->rc_row;
620 
621           rc->rc_ops->copyrows(rc->rc_cookie, rc->rc_row + n, rc->rc_row,
622               rc->rc_maxrow - rc->rc_row - n);
623 
624           rc->rc_ops->eraserows(rc->rc_cookie, rc->rc_maxrow - n, n,
625               rc->rc_attr);
626 }
627 
628 /* Insert some characters */
629 void
rcons_insertchar(struct rconsole * rc,int n)630 rcons_insertchar(struct rconsole *rc, int n)
631 {
632           /* Can't insert more chars than can fit */
633           if (n > rc->rc_maxcol - rc->rc_col)
634                     n = rc->rc_maxcol - rc->rc_col - 1;
635 
636           rc->rc_ops->copycols(rc->rc_cookie, rc->rc_row, rc->rc_col,
637               rc->rc_col + n, rc->rc_maxcol - rc->rc_col - n - 1);
638 
639           rc->rc_ops->erasecols(rc->rc_cookie, rc->rc_row, rc->rc_col,
640               n, rc->rc_attr);
641 }
642 
643 /* Insert some lines */
644 void
rcons_insertline(struct rconsole * rc,int n)645 rcons_insertline(struct rconsole *rc, int n)
646 {
647           /* Can't insert more lines than can fit */
648           if (n > rc->rc_maxrow - rc->rc_row)
649                     n = rc->rc_maxrow - rc->rc_row;
650 
651           rc->rc_ops->copyrows(rc->rc_cookie, rc->rc_row, rc->rc_row + n,
652               rc->rc_maxrow - rc->rc_row - n);
653 
654           rc->rc_ops->eraserows(rc->rc_cookie, rc->rc_row, n,
655               rc->rc_attr);
656 }
657 
658 /* end of rcons_subr.c */
659