home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume4 / se / part4 / screen.c next >
Encoding:
C/C++ Source or Header  |  1986-11-30  |  22.4 KB  |  1,101 lines

  1. /*
  2. ** screen.c
  3. **
  4. ** screen handling functions for the screen editor.
  5. */
  6.  
  7.  
  8. #include "se.h"
  9. #include "extern.h"
  10. #include <time.h>
  11.  
  12. /* clrrow --- clear out all of a row except the bar */
  13.  
  14. clrrow (row)
  15. register int row;
  16. {
  17.     loadstr ("", row, 0, BARCOL - 1);
  18.     loadstr ("", row, BARCOL + 1, Ncols - 1);
  19. }
  20.  
  21.  
  22.  
  23. /* display_message --- copy contents of message file to screen */
  24.  
  25. display_message (fp)
  26. FILE *fp;
  27. {
  28.     char lin[MAXCOLS];
  29.     char *fgets ();
  30.     int row, col, eof, k;
  31.     static char more[] = " M O R E   T O   C O M E ";
  32.  
  33.     if (Toprow > 0)
  34.     {
  35.         Topln = max (0, Topln - (Toprow - 1));
  36.         Toprow = 0;
  37.     }
  38.  
  39.     eof = NO;
  40.     for (row = Toprow; row <= Botrow; row++)
  41.     {
  42.         if (fgets (lin, Ncols, fp) == NULL)
  43.         {
  44.             eof = YES;
  45.             break;
  46.         }
  47.         Toprow++;
  48.         Topln++;
  49.         lin[strlen (lin)] = EOS;        /* remove '\n' */
  50.         loadstr (lin, row, 0, Ncols);
  51.     }
  52.  
  53.     if (eof == NO)
  54.     {
  55.         k = (Ncols - strlen (more)) / 2;
  56.         for (col = 0; col < k; col++)
  57.             load ('*', row, col);
  58.  
  59.         for (k = 0; more[k] != EOS; k++, col++)
  60.             load (more[k], row, col);
  61.  
  62.         for (; col < Ncols; col++)
  63.             load ('*', row, col);
  64.  
  65.         Toprow++;
  66.         Topln++;
  67.         row++;
  68.     }
  69.  
  70.     for (col = 0; col < Ncols; col++)
  71.         load ('-', row, col);
  72.     Toprow++;
  73.     Topln++;
  74.  
  75.     if (Topln > Lastln)
  76.         adjust_window (0, Lastln);
  77.  
  78.     if (Curln < Topln)
  79.         Curln = Topln;
  80.  
  81.     First_affected = Topln;        /* must rewrite the whole screen */
  82.  
  83.     mesg ("Enter o- to restore display", HELP_MSG);
  84.  
  85.     if (eof == YES)
  86.         return (EOF);
  87.  
  88.     return (OK);
  89. }
  90.  
  91. static char smargin[] = "MARGIN";
  92.  
  93. /* getcmd --- read a line from the terminal (for se) */
  94.  
  95. getcmd (lin, col1, curpos, termchar)
  96. char *lin, *termchar;
  97. int col1, *curpos;
  98. {
  99.     int cursor, nlpos, prev_cursor, prev_status, status,
  100.     scan_pos, tab_pos, first, strlen (),
  101.     scan_tab (), scan_char (), cread ();
  102.     char c;
  103.  
  104.     nlpos = strlen (lin) - 1;
  105.     if (nlpos == -1 || lin[nlpos] != '\n')
  106.         nlpos++;
  107.  
  108.     if (*curpos < 0)
  109.         cursor = 0;
  110.     else if (*curpos >= MAXLINE - 1)
  111.         cursor = nlpos;
  112.     else
  113.         set_cursor (*curpos, &status, &cursor, &nlpos, lin);
  114.     prev_cursor = cursor;
  115.  
  116.     watch ();    /* display the time of day */
  117.  
  118.     switch (Nchoise) {    /* update the line number display */
  119.     case CURLINE:
  120.         litnnum ("line ", Curln, LINE_MSG);
  121.         break;
  122.     case LASTLINE:
  123.         litnnum ("$ = ", Lastln, LINE_MSG);
  124.         break;
  125.     case TOPLINE:
  126.         litnnum ("# = ", Topln, LINE_MSG);
  127.         break;
  128.     default:
  129.         mesg ("", LINE_MSG);
  130.         break;
  131.     }
  132.  
  133.     if (cursor + 1 < Warncol)    /* erase the column display */
  134.         mesg ("", COL_MSG);
  135.  
  136.     *termchar = EOS;    /* not yet terminated */
  137.     status = OK;
  138.     prev_status = ERR;
  139.     first = col1;
  140.  
  141.     while (*termchar == EOS)
  142.     {
  143.         lin[nlpos] = EOS;    /* make sure the line has an EOS */
  144.         if (status == ERR)    /* last iteration generated an error */
  145.             twrite (1, "\007", 1);    /* Bell */
  146.         else if (prev_status == ERR)    /* last one OK but one before had error */
  147.             mesg ("", CHAR_MSG);
  148.  
  149.         prev_status = status;
  150.         status = OK;
  151.  
  152.         if (first > cursor)     /* do horiz. scroll if needed */
  153.             first = cursor;
  154.         else if (first < cursor - Ncols + POOPCOL + 1)
  155.             first = cursor - Ncols + POOPCOL + 1;
  156.  
  157.         if (first == col1)      /* indicate horizontally shifted line */
  158.             load ('|', Cmdrow, BARCOL);
  159.         else if (first > col1)
  160.             load ('<', Cmdrow, BARCOL);
  161.         else if (first < col1)
  162.             load ('>', Cmdrow, BARCOL);
  163.         loadstr (&lin[first], Cmdrow, POOPCOL, Ncols - 1);
  164.  
  165.         if (cursor == Warncol - 1 && prev_cursor < Warncol - 1)
  166.             twrite (1, "\007", 1);   /* Bell */
  167.         if (cursor >= Warncol - 1)
  168.             litnnum ("col ", cursor + 1, COL_MSG);
  169.         else if (prev_cursor >= Warncol - 1)
  170.             mesg ("", COL_MSG);
  171.  
  172.         position_cursor (Cmdrow, cursor + POOPCOL - first);
  173.         prev_cursor = cursor;
  174.  
  175.         /* get a character  */
  176.         switch (c = cread()) {        /* branch on character value */
  177.  
  178.     /* Literal characters: */
  179.         case ' ': case '!': case '"': case '#': case '$': case '%': 
  180.         case '&': case '\'': case '(': case ')': case '*': case '+':
  181.         case ',': case '-': case '.': case '/': case '0': case '1': 
  182.         case '2': case '3': case '4': case '5': case '6': case '7':
  183.         case '8': case '9': case ':': case ';': case '<': case '=': 
  184.         case '>': case '?': case '@': case '[': case '\\': case ']':
  185.         case '^': case '_': case '`': case '{': case '|': case '}': 
  186.         case '~': case 'A': case 'B': case 'C': case 'D': case 'E':
  187.         case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': 
  188.         case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q':
  189.         case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': 
  190.         case 'X': case 'Y': case 'Z': case 'a': case 'b': case 'c':
  191.         case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': 
  192.         case 'j': case 'k': case 'l': case 'm': case 'n': case 'o':
  193.         case 'p': case 'q': case 'r': case 's': case 't': case 'u': 
  194.         case 'v': case 'w': case 'x': case 'y': case 'z': case ESC:
  195.             if (c == ESC) /* take next char literally */
  196.             {
  197.                 if ((c = cread()) == '\r')
  198.                     c = '\n';
  199.             }
  200.             else if (Invert_case == YES && isalpha (c))
  201.                 c ^= 040;       /* toggle case (ASCII only) */
  202.             if (Insert_mode == YES)
  203.                 insert (1, &nlpos, &status, cursor, lin);
  204.             else if (cursor >= MAXLINE - 2)
  205.             {
  206.                 status = ERR;
  207.                 mesg (smargin, CHAR_MSG);
  208.             }
  209.             if (status != ERR)
  210.             {
  211.                 lin[cursor++] = c;
  212.                 if (nlpos < cursor)
  213.                     nlpos = cursor;
  214.                 if (c == '\n')
  215.                     *termchar = CURSOR_SAME;
  216.             }
  217.             break;
  218.  
  219.     /* Leftward cursor functions: */
  220.         case CURSOR_LEFT:
  221.             set_cursor (cursor - 1, &status, &cursor, &nlpos, lin);
  222.             break;
  223.  
  224.         case TAB_LEFT:
  225.             tab_pos = scan_tab (TAB_LEFT, cursor, &status);
  226.             if (status != ERR)
  227.                 cursor = tab_pos;
  228.             break;
  229.  
  230.         case SKIP_LEFT:
  231.             cursor = 0;
  232.             break;
  233.  
  234.         case SCAN_LEFT:
  235.             scan_pos = scan_char (c, YES, cursor, nlpos,
  236.                 lin, &status);
  237.             if (status != ERR)
  238.                 cursor = scan_pos;
  239.             break;
  240.  
  241.         case G_LEFT:
  242.             set_cursor (cursor - 1, &status, &cursor, &nlpos, lin);
  243.             if (status != ERR)
  244.                 gobble (1, cursor, &status, &nlpos, lin);
  245.             break;
  246.  
  247.         case G_TAB_LEFT:
  248.             tab_pos = scan_tab (TAB_LEFT, cursor, &status);
  249.             if (status != ERR)
  250.             {
  251.                 cursor = tab_pos;
  252.                 gobble (prev_cursor - tab_pos, cursor,
  253.                     &status, &nlpos, lin);
  254.             }
  255.             break;
  256.  
  257.         case KILL_LEFT:
  258.             cursor = 0;
  259.             gobble (prev_cursor /* - 1 */, cursor, &status, &nlpos, lin);
  260.             break;
  261.  
  262.         case G_SCAN_LEFT:
  263.             scan_pos = scan_char (c, NO, cursor, nlpos,
  264.                 lin, &status);
  265.             if (status != ERR)
  266.             {
  267.                 cursor = scan_pos;
  268.                 gobble (prev_cursor - scan_pos, cursor,
  269.                     &status, &nlpos, lin);
  270.             }
  271.             break;
  272.  
  273.     /* Rightward cursor functions: */
  274.         case CURSOR_RIGHT:
  275.             set_cursor (cursor + 1, &status, &cursor, &nlpos, lin);
  276.             break;
  277.  
  278.         case TAB_RIGHT:
  279.             tab_pos = scan_tab (TAB_RIGHT, cursor, &status);
  280.             if (status != ERR)
  281.                 set_cursor (tab_pos, &status, &cursor,
  282.                     &nlpos, lin);
  283.             break;
  284.  
  285.         case SKIP_RIGHT:
  286.             cursor = nlpos;
  287.             first = col1;
  288.             break;
  289.  
  290.         case SCAN_RIGHT:
  291.             scan_pos = scan_char (c, YES, cursor, nlpos,
  292.                 lin, &status);
  293.             if (status != ERR)
  294.                 cursor = scan_pos;
  295.             break;
  296.  
  297.         case G_RIGHT:
  298.             gobble (1, cursor, &status, &nlpos, lin);
  299.             break;
  300.  
  301.         case G_TAB_RIGHT:
  302.             tab_pos = scan_tab (TAB_RIGHT, cursor,
  303.                 &status);
  304.             if (status != ERR)
  305.                 gobble (tab_pos - cursor, cursor, &status,
  306.                     &nlpos, lin);
  307.             break;
  308.  
  309.         case KILL_RIGHT:
  310.             gobble (nlpos - cursor, cursor, &status, &nlpos, lin);
  311.             break;
  312.  
  313.         case G_SCAN_RIGHT:
  314.             scan_pos = scan_char (c, NO, cursor, nlpos,
  315.                 lin, &status);
  316.             if (status != ERR)
  317.                 gobble (scan_pos - cursor, cursor, &status,
  318.                     &nlpos, lin);
  319.             break;
  320.  
  321.     /* Line termination functions: */
  322.         case T_SKIP_RIGHT:
  323.             cursor = nlpos;
  324.             *termchar = c;
  325.             break;
  326.  
  327.         case T_KILL_RIGHT:
  328.             nlpos = cursor;
  329.             *termchar = c;
  330.             break;
  331.  
  332.         case FUNNY:
  333.         case CURSOR_UP:
  334.         case CURSOR_DOWN:
  335.             *termchar = c;
  336.             break;
  337.  
  338.     /* Insertion functions: */
  339.         case INSERT_BLANK:
  340.             insert (1, &nlpos, &status, cursor, lin);
  341.             if (status != ERR)
  342.                 lin[cursor] = ' ';
  343.             break;
  344.  
  345.         case INSERT_NEWLINE:
  346.             insert (1, &nlpos, &status, cursor, lin);
  347.             if (status != ERR)
  348.             {
  349.                 lin[cursor] = '\n';
  350.                 *termchar = CURSOR_UP;
  351.             }
  352.             break;
  353.  
  354.         case INSERT_TAB:
  355.             while (lin[cursor] == ' ' || lin[cursor] == '\t')
  356.                 cursor++;
  357.             tab_pos = scan_tab (TAB_RIGHT, cursor, &status);
  358.             if (status != ERR)
  359.                 insert (tab_pos - cursor, &nlpos, &status,
  360.                     cursor, lin);
  361.             if (status != ERR)
  362.                 for (; cursor < tab_pos; cursor++)
  363.                     lin[cursor] = ' ';
  364.             cursor = prev_cursor;
  365.             break;
  366.  
  367.     /* Miscellanious control functions: */
  368.         case TOGGLE_INSERT_MODE:
  369.             Insert_mode = YES + NO - Insert_mode;
  370.             if (Insert_mode == NO)
  371.                 mesg ("", INS_MSG);
  372.             else
  373.                 mesg ("INSERT", INS_MSG);
  374.             break;
  375.  
  376.         case SHIFT_CASE:
  377.             Invert_case = YES + NO - Invert_case;
  378.             if (Invert_case == NO)
  379.                 mesg ("", CASE_MSG);
  380.             else
  381.                 mesg ("CASE", CASE_MSG);
  382.             break;
  383.  
  384.         case KILL_ALL:
  385.             nlpos = cursor = 0;
  386.             break;
  387.  
  388.         case FIX_SCREEN:
  389.             restore_screen ();
  390.             break;
  391.  
  392.         default:
  393.             status = ERR;
  394.             mesg ("WHA?", CHAR_MSG);
  395.             break;
  396.         } /* end switch */
  397.     } /* while (termchar == EOS) */
  398.  
  399.     lin[nlpos] = '\n';
  400.     lin[nlpos + 1] = EOS;
  401.  
  402.     load ('|', Cmdrow, BARCOL);
  403.     if (nlpos <= col1)
  404.         loadstr ("", Cmdrow, POOPCOL, Ncols - 1);
  405.     else
  406.         loadstr (&lin[col1], Cmdrow, POOPCOL, Ncols - 1);
  407.  
  408.     if (cursor >= Warncol - 1)
  409.         litnnum ("col ", cursor + 1, COL_MSG);
  410.     else if (prev_cursor >= Warncol - 1)
  411.         mesg ("", COL_MSG);
  412.  
  413.     *curpos = cursor;
  414. }
  415.  
  416.  
  417.  
  418. /* cread --- read a character, handle interrupts and hangups */
  419. /*              ALL keyboard input should pass through this routine */
  420.  
  421. int cread ()
  422. {
  423.     char c;
  424.     int read ();
  425.  
  426.     tflush ();
  427.     if (Peekc)
  428.     {
  429.         c = Peekc;
  430.         Peekc = EOS;
  431.     }
  432.     else
  433.     {
  434. #ifdef BSD4_2
  435.         Reading = YES;
  436. #endif
  437.         if (read (0, &c, 1) == -1)
  438.             if (Hup_caught)
  439.                 hangup ();
  440.             else    /* must be a SIGINT at present */
  441.             {
  442.                 Int_caught = 0;
  443.                 Errcode = ENOERR;
  444.                 c = '\177';
  445.             }
  446. #ifdef BSD4_2
  447.         Reading = NO;
  448. #endif
  449.     }
  450.  
  451.     return c;
  452. }
  453.  
  454.  
  455.  
  456. /* scan_char --- scan current line for a character */
  457.  
  458. static int scan_char (chr, wrap, cursor, nlpos, lin, status)
  459. char chr, *lin;
  460. int wrap, cursor, nlpos, *status;
  461. {
  462.     register char c;
  463.     register int inc, scan_pos;
  464.     int cread ();
  465.  
  466.     c = cread ();
  467.     if (Invert_case == YES && isalpha (c))
  468.         c ^= 040;       /* toggle case */
  469.     if (c == chr)
  470.         c = Last_char_scanned;
  471.     Last_char_scanned = c;
  472.  
  473.     if (chr == SCAN_LEFT || chr == G_SCAN_LEFT)
  474.         inc = -1;
  475.     else
  476.         inc = 1;
  477.  
  478.     /* NOTE:  modify this code AT YOUR OWN RISK! */
  479.     scan_pos = cursor;
  480.     do
  481.     {
  482.         if (scan_pos < 0)
  483.             if (wrap == NO)
  484.                 break;
  485.             else
  486.                 scan_pos = nlpos;
  487.         else if (scan_pos > nlpos)
  488.             if (wrap == NO)
  489.                 break;
  490.             else
  491.                 scan_pos = 0;
  492.         else
  493.             scan_pos += inc;
  494.         if (-1 < scan_pos && scan_pos < nlpos && lin[scan_pos] == c)
  495.             break;
  496.     } while (scan_pos != cursor);
  497.  
  498.     if (scan_pos < 0 || scan_pos >= nlpos || lin[scan_pos] != c)
  499.     {
  500.         *status = ERR;
  501.         mesg ("NOCHAR", CHAR_MSG);
  502.     }
  503.  
  504.     return (scan_pos);
  505. }
  506.  
  507.  
  508.  
  509. static int scan_tab (chr, cursor, status)
  510. char chr;
  511. int cursor, *status;
  512. {
  513.     register int inc, tab_pos;
  514.  
  515.     if (chr == TAB_LEFT)
  516.     {
  517.         inc = -1;
  518.         tab_pos = cursor - 1;
  519.     }
  520.     else
  521.     {
  522.         inc = 1;
  523.         tab_pos = cursor + 1;
  524.     }
  525.  
  526.     for (; -1 < tab_pos && tab_pos < MAXLINE; tab_pos += inc)
  527.         if (Tabstops[tab_pos] == YES)
  528.             break;
  529.  
  530.     if (tab_pos < 0 || tab_pos >= MAXLINE - 1)
  531.     {
  532.         *status = ERR;
  533.         mesg (smargin, CHAR_MSG);
  534.     }
  535.  
  536.     return (tab_pos);
  537. }
  538.  
  539.  
  540.  
  541. /* gobble --- delete characters starting at the current cursor position */
  542.  
  543. static gobble (len, cursor, status, nlpos, lin)
  544. int len, cursor, *status, *nlpos;
  545. char *lin;
  546. {
  547.     if (cursor + len > *nlpos)
  548.     {
  549.         *status = ERR;
  550.         mesg (smargin, CHAR_MSG);
  551.     }
  552.     else if (len > 0)
  553.     {
  554.         strcpy (&lin[cursor], &lin[cursor + len]);
  555.         *nlpos -= len;
  556.     }
  557. }
  558.  
  559.  
  560.  
  561. /* insert --- shift characters right starting at the current cursor position */
  562.  
  563. static insert (len, nlpos, status, cursor, lin)
  564. register int len, *nlpos, cursor;
  565. int *status;
  566. register char *lin;
  567. {
  568.     register int fr, to;
  569.  
  570.     if (*nlpos + len >= MAXLINE - 1)
  571.     {
  572.         *status = ERR;
  573.         mesg (smargin, CHAR_MSG);
  574.     }
  575.     else
  576.     {
  577.         for (fr = *nlpos, to = *nlpos + len; fr >= cursor; fr--, to--)
  578.             lin[to] = lin[fr];
  579.         *nlpos += len;
  580.     }
  581. }
  582.  
  583.  
  584.  
  585. /* set_cursor --- move the cursor, extend line if necessary */
  586.  
  587. static set_cursor (pos, status, cursor, nlpos, lin)
  588. register int pos, *status, *cursor, *nlpos;
  589. register char *lin;
  590. {
  591.     if (pos < 0 || pos >= MAXLINE - 1)
  592.     {
  593.         *status = ERR;
  594.         mesg (smargin, CHAR_MSG);
  595.     }
  596.     else
  597.     {
  598.         *cursor = pos;
  599.         for (; *nlpos < *cursor; (*nlpos)++)
  600.             lin[*nlpos] = ' ';
  601.     }
  602. }
  603.  
  604.  
  605. /* litnnum --- display literal and number in message area */
  606.  
  607. litnnum (lit, num, type)
  608. register char *lit;
  609. register int num, type;
  610. {
  611.     char msg[MAXLINE];
  612.  
  613.     sprintf (msg, "%s%d", lit, num);
  614.     mesg (msg, type);
  615. }
  616.  
  617.  
  618.  
  619. /* load --- load a character onto the screen at given coordinates */
  620.  
  621. load (chr, row, col)
  622. register char chr;
  623. register int row, col;
  624. {
  625.     register char ch;
  626.  
  627.     ch = (chr < ' ' || chr >= DEL) ? Unprintable : chr;
  628.  
  629.     if (row >= 0 && row < Nrows && col >= 0 && col < Ncols
  630.         && Screen_image[row][col] != ch)
  631.     {
  632.         position_cursor (row, col);
  633.         Screen_image[row][col] = ch;
  634.         send (ch);
  635.     }
  636. }
  637.  
  638.  
  639. /* loadstr --- load a string into a field of the screen */
  640.  
  641. loadstr (str, row, stcol, endcol)
  642. register char *str;
  643. int row, stcol, endcol;
  644. {
  645.     register char ch;
  646.     register int p, c, limit;
  647.  
  648.     if (row >= 0 && row < Nrows && stcol >= 0)
  649.     {
  650.         c = stcol;
  651.         for (p = 0; str[p] != EOS; p++)
  652.         {
  653.             if (c >= Ncols)
  654.                 break;
  655.             ch = str[p];
  656.             if (ch < ' ' || ch >= DEL)
  657.                 ch = Unprintable;
  658.             if (Screen_image[row][c] != ch)
  659.             {
  660.                 Screen_image[row][c] = ch;
  661.                 position_cursor (row, c);
  662.                 send (ch);
  663.             }
  664.             c++;
  665.         }
  666.         if (endcol >= Ncols - 1 && c < Ncols - 1)
  667.             clear_to_eol (row, c);
  668.         else
  669.         {
  670.             limit = (endcol < Ncols - 1) ? endcol : Ncols - 1;
  671.             for (; c <= limit; c++)
  672.                 if (Screen_image[row][c] != ' ')
  673.                 {
  674.                     Screen_image[row][c] = ' ';
  675.                     position_cursor (row, c);
  676.                     send (' ');
  677.                 }
  678.         }
  679.     }
  680. }
  681.  
  682.  
  683. /* mesg --- display a message in the status row */
  684.  
  685. mesg (s, t)
  686. char *s;
  687. register int t;
  688. {
  689.     register int col, need, c, first, last;
  690.     int strlen ();
  691.  
  692.     for (first = 0; first < Ncols; first++)
  693.         if (Msgalloc[first] == t)
  694.             break;
  695.     for (last = first; last < Ncols; last++)
  696.     {
  697.         if (Msgalloc[last] != t)
  698.             break;
  699.         Msgalloc[last] = NOMSG;
  700.     }
  701.     for (; first > 0 && Msgalloc[first - 1] == NOMSG; first--)
  702.         ;
  703.  
  704.     need = strlen (s) + 2;  /* for two blanks */
  705.  
  706.     if (need > 2)           /* non-empty message */
  707.     {
  708.         if (need <= last - first)       /* fits in previous slot */
  709.             col = first;        /* keep it there */
  710.         else        /* needs a new slot */
  711.             for (col = 0; col < Ncols - 1; col = c)
  712.             {
  713.                 while (col < Ncols - 1
  714.                     && Msgalloc[col] != NOMSG)
  715.                     col++;
  716.                 for (c = col; Msgalloc[c] == NOMSG; c++)
  717.                     if (c >= Ncols - 1)
  718.                         break;
  719.                 if (c - col >= need)
  720.                     break;
  721.             }
  722.  
  723.         if (col + need >= Ncols)        /* have to collect garbage */
  724.         {
  725.             col = 0;
  726.             for (c = 0; c < Ncols; c++)
  727.                 if (Msgalloc[c] != NOMSG)
  728.                 {
  729.                     load (Screen_image[Nrows - 1][c],
  730.                         Nrows - 1, col);
  731.                     Msgalloc[col] = Msgalloc[c];
  732.                     col++;
  733.                 }
  734.             for (c = col; c < Ncols; c++)
  735.                 Msgalloc[c] = NOMSG;
  736.         }
  737.  
  738.         load (' ', Nrows - 1, col);
  739.         loadstr (s, Nrows - 1, col + 1, 0);
  740.         load (' ', Nrows - 1, col + need - 1);
  741.         last = col + need - 1;
  742.         if (last > Ncols - 1)
  743.             last = Ncols - 1;
  744.         for (c = col; c <= last; c++)
  745.             Msgalloc[c] = t;
  746.     }
  747.  
  748.     for (col = 0; col < Ncols; col++)
  749.         if (Msgalloc[col] == NOMSG)
  750.             load ('.', Nrows - 1, col);
  751.     tflush ();
  752. }
  753.  
  754. /* prompt --- load str into margin of command line */
  755.  
  756. prompt (str)
  757. char *str;
  758. {
  759.     loadstr (str, Cmdrow, 0, BARCOL - 1);
  760.     tflush ();
  761. }
  762.  
  763.  
  764.  
  765. /* restore_screen --- screen has been garbaged; fix it */
  766.  
  767. restore_screen ()
  768. {
  769.     register int row, col;
  770.     int intrpt ();
  771.  
  772.     clrscreen ();
  773.     for (row = 0; row < Nrows && ! intrpt (); row++)
  774.         for (col = 0; col < Ncols; col++)
  775.             if (Screen_image[row][col] != ' ')
  776.             {
  777.                 position_cursor (row, col);
  778.                 send (Screen_image[row][col]);
  779.             }
  780.  
  781.     remark ("");    /* get rid of 'type control-q....' */
  782. }
  783.  
  784. /* saynum --- display a number in the message area */
  785.  
  786. saynum (n)
  787. int n;
  788. {
  789.     char s[MAXLINE];
  790.  
  791.     sprintf (s, "%d", n);
  792.     remark (s);
  793. }
  794.  
  795. /* updscreen --- update screen from edit buffer */
  796.  
  797. updscreen ()
  798. {
  799.     char abs_lineno[10], rel_lineno[10];
  800.     register int line, row;
  801.     register int i;
  802.     int prevln ();
  803.     register LINEDESC *k;
  804.     LINEDESC *getind ();
  805.  
  806.     fixscreen ();
  807.  
  808.     line = Topln;
  809.     k = getind (line);
  810.  
  811.     for (row = Toprow; row <= Botrow; row++)
  812.     {
  813.         if (line > Lastln || line < 1)
  814.         {
  815.             loadstr ("", row, 0, BARCOL - 1);
  816.             load ('|', row, BARCOL);
  817.             loadstr ("", row, BARCOL + 1, Ncols - 1);
  818.         }
  819.         else
  820.         {
  821.             if (line == Curln)
  822.                 loadstr (".  ->", row, 0, NAMECOL - 1);
  823.             else if (line == 1)
  824.                 loadstr ("1", row, 0, NAMECOL - 1);
  825.             else if (line == Lastln)
  826.                 loadstr ("$", row, 0, NAMECOL - 1);
  827.             else if (Absnos == NO && row <= 25)
  828.             {
  829.                 rel_lineno[0] = Rel_a + row;
  830.                 rel_lineno[1] = EOS;
  831.                 loadstr (rel_lineno, row, 0, NAMECOL - 1);
  832.             }
  833.             else
  834.             {
  835.                 sprintf (abs_lineno, "%d", line);
  836.                 loadstr (abs_lineno, row, 0, NAMECOL - 1);
  837.             }
  838.  
  839.             load ((char) k->Markname, row, NAMECOL);
  840.             load ('|', row, BARCOL);
  841.  
  842.             if (line >= First_affected)
  843.             {
  844.                 gtxt (k);
  845.                 if (Firstcol >= k->Lineleng)
  846.                     loadstr ("", row, POOPCOL, Ncols - 1);
  847.                 else
  848.                     loadstr (&Txt[Firstcol], row, POOPCOL,
  849.                         Ncols - 1);
  850.             }
  851.         }
  852.  
  853.         line++;
  854.         k = NEXTLINE(k);
  855.     }
  856.  
  857.     First_affected = Lastln;
  858.  
  859.     Sctop = Topln;
  860.     Sclen = Botrow - Toprow + 1;
  861.     for (i = 0; i < Sclen; i++)
  862.         Scline[i] = Sctop + i <= Lastln ? i : -1;
  863. }
  864.  
  865.  
  866.  
  867. /* warn_deleted --- indicate which rows on screen are no longer valid */
  868.  
  869. warn_deleted (from, to)
  870. register int from, to;
  871. {
  872.     register int row;
  873.  
  874.     for (row = Toprow; row <= Botrow; row++)
  875.         if (Topln + row - Toprow >= from
  876.             && Topln + row - Toprow <= to)
  877.             loadstr ("gone", row, 0, BARCOL - 1);
  878. }
  879.  
  880.  
  881. /* watch --- keep time displayed on screen for users without watches */
  882.  
  883. watch ()
  884. {
  885.     long clock;
  886.     struct tm *now, *localtime ();
  887.     char face[10];
  888.  
  889.     time (&clock);
  890.     now = localtime (&clock);
  891.     sprintf (face, "%02d:%02d", now->tm_hour, now->tm_min);
  892.     mesg (face, TIME_MSG);
  893. }
  894.  
  895. /* adjust_window --- position window to include lines 'from' through 'to' */
  896.  
  897. adjust_window (from, to)
  898. register int from, to;
  899. {
  900.     register int i, l1, l2, hw;
  901.     int hwinsdel();
  902.  
  903.     /* see if the whole range of lines is on the screen */
  904.     if (from < Topln || to > Topln + (Botrow - Toprow))
  905.     {
  906.         /* find the first and last lines that are on the screen */
  907.         for (i = 0; i < Sclen && Scline[i] == -1; i++)
  908.             ;
  909.         l1 = i < Sclen ? Scline[i] + Sctop : Lastln + 1;
  910.         for (i = Sclen - 1; i >= 0 && Scline[i] == -1; i--)
  911.             ;
  912.         l2 = i >= 0 ? Scline[i] + Sctop : 0;
  913.  
  914.         /* see if we have hardware line insert/delete */
  915.         hw = hwinsdel();
  916.  
  917.         /* now find the best place to move the screen */
  918.         if (to - from > Botrow - Toprow)
  919.             Topln = to - (Botrow - Toprow);     /* put last part on screen */
  920.         else if (hw && from >= l1 && to <= l2)
  921.             Topln = (l1 + l2 + Toprow - Botrow) / 2;/* center l1 through l2 */
  922.         else if (hw && from < l1 && l1 - from < (Botrow - Toprow + 1) / 2)
  923.             Topln = from;                       /* slide the screen down */
  924.         else if (hw && to > l2 && to - l2 < (Botrow - Toprow + 1) / 2)
  925.             Topln = to - (Botrow - Toprow);     /* slide the screen up */
  926.         else
  927.             Topln = (from + to + Toprow - Botrow) / 2; /* center the range */
  928.         if (Topln + (Botrow - Toprow) > Lastln)
  929.             Topln = Lastln - (Botrow - Toprow);
  930.         if (Topln < 1)
  931.             Topln = 1;
  932.         if (First_affected > Topln)
  933.             First_affected = Topln;
  934.     }
  935. }
  936.  
  937.  
  938. /* svdel --- record the deletion of buffer lines for screen update */
  939.  
  940. svdel (ln, n)
  941. register int ln, n;
  942. {
  943.     register int i, j, lb, ub;
  944.  
  945.     if (ln + n <= Sctop)
  946.         Sctop -= n;
  947.     else if (ln < Sctop)
  948.     {
  949.         ub = ln + n - Sctop;
  950.         for (i = 0; i < Sclen; i++)
  951.             if (Scline[i] == -1)
  952.                 ;
  953.             else if (Scline[i] < ub)
  954.                 Scline[i] = -1;
  955.             else
  956.                 Scline[i] -= ub;
  957.         Sctop = ln;
  958.     }
  959.     else
  960.     {
  961.         lb = ln - Sctop;
  962.         ub = ln + n - Sctop;
  963.         for (i = 0; i < Sclen; i++)
  964.             if (Scline[i] == -1 || Scline[i] < lb)
  965.                 ;
  966.             else if (Scline[i] < ub)
  967.                 Scline[i] = -1;
  968.             else
  969.                 Scline[i] -= n;
  970.     }
  971. }
  972.  
  973.  
  974. /* svins --- record a buffer insertion for screen updating */
  975.  
  976. svins (ln, n)
  977. register int ln, n;
  978. {
  979.     register int i, lb;
  980.  
  981.     if (ln < Sctop)
  982.         Sctop += n;
  983.     else
  984.     {
  985.         lb = ln - Sctop;
  986.         for (i = 0; i < Sclen; i++)
  987.             if (Scline[i] != -1 && Scline[i] > lb)
  988.                 Scline[i] += n;
  989.     }
  990.  
  991. }
  992.  
  993.  
  994. /* fixscreen --- try to patch up the screen using insert/delete line */
  995.  
  996. fixscreen ()
  997. {
  998.     register int oi;    /* "old" screen index */
  999.     register int ni;    /* "new" screen index */
  1000.     register int dc;    /* number of deletions in current block */
  1001.     register int ic;    /* number of insertions in current block */
  1002.     register int ul;    /* number of lines that must be rewritten if */
  1003.                 /* we don't update by insert/delete */
  1004.     int wline[MAXROWS];    /* new screen contents; Scline contains old */
  1005.     int p;
  1006.  
  1007.     /* if the screen width was changed, give up before it's too late */
  1008.     if (Botrow - Toprow + 1 != Sclen || ! hwinsdel())
  1009.         return;
  1010.  
  1011.     /* scale the offsets in Scline to the new Topln; set any that are */
  1012.     /* off the screen to -1 so we won't have to fool with them */
  1013.     for (oi = 0; oi < Sclen; oi++)
  1014.         if (Scline[oi] == -1 || Scline[oi] + Sctop < Topln
  1015.             || Scline[oi] + Sctop > Topln + (Botrow - Toprow))
  1016.             Scline[oi] = -1;
  1017.         else
  1018.             Scline[oi] += Sctop - Topln;
  1019.  
  1020.     /* fill in wline with only those numbers that are in Scline */
  1021.     for (oi = 0, ni = 0; ni < Sclen; ni++)
  1022.     {
  1023.         while (oi < Sclen && Scline[oi] == -1)
  1024.             oi++;
  1025.         if (oi < Sclen && Scline[oi] == ni)
  1026.         {
  1027.             wline[ni] = ni;
  1028.             oi++;
  1029.         }
  1030.         else
  1031.             wline[ni] = -1;
  1032.     }
  1033.  
  1034.     /* see if it's still advisable to fix the screen: if the number */
  1035.     /* of lines that must be rewritten is less than 2 + the number */
  1036.     /* of lines that must be inserted, don't bother (this is a dumb */
  1037.     /* but fairly effective heuristic) */
  1038.     ul = ni = 0;
  1039.     for (oi = 0; oi < Sclen; oi++)
  1040.     {
  1041.         if (Scline[oi] != wline[oi] || Scline[oi] == -1)
  1042.             ul++;
  1043.         if (wline[oi] == -1)
  1044.             ni++;
  1045.     }
  1046.     if (ul < ni + 2)
  1047.         return;
  1048.  
  1049.     /* Now scan the screens backwards and put out delete-lines */
  1050.     /* for all deletions and changes with more old lines than new */
  1051.     oi = ni = Sclen - 1;
  1052.     while (oi >= 0 || ni >= 0)
  1053.     {
  1054.         for (dc = 0; oi >= 0 && Scline[oi] == -1; dc++)
  1055.             oi--;
  1056.         for (ic = 0; ni >= 0 && wline[ni] == -1; ic++)
  1057.             ni--;
  1058.         if (dc > ic)
  1059.             dellines (oi + 1 + Toprow + ic, dc - ic);
  1060.         while (oi >= 0 && Scline[oi] != -1
  1061.             && ni >= 0 && wline[ni] == Scline[oi])
  1062.             oi--, ni--;
  1063.     }
  1064.  
  1065.     /* At last, scan the screens forward and put out insert-lines */
  1066.     /* for all insertions and changes with more new lines than old */
  1067.     oi = ni = 0;
  1068.     while (oi < Sclen || ni < Sclen)
  1069.     {
  1070.         p = ni;
  1071.         for (dc = 0; oi < Sclen && Scline[oi] == -1; dc++)
  1072.             oi++;
  1073.         for (ic = 0; ni < Sclen && wline[ni] == -1; ic++)
  1074.             ni++;
  1075.         if (ic > dc)
  1076.             inslines (p + Toprow + dc, ic - dc);
  1077.         while (oi < Sclen && Scline[oi] != -1
  1078.             && ni < Sclen && wline[ni] == Scline[oi])
  1079.             oi++, ni++;
  1080.     }
  1081.  
  1082. }
  1083.  
  1084.  
  1085. #ifdef HARD_TERMS
  1086. /* senddelay --- send NULs to delay n milliseconds */
  1087.  
  1088. senddelay (n)
  1089. int n;
  1090. {
  1091.     register int q;
  1092.  
  1093.     q = (long) n * Tspeed / 1000l;
  1094.     while (q > 0)
  1095.     {
  1096.         twrite (1, "\0\0\0\0\0\0\0\0\0\0", q > 10 ? 10 : q);
  1097.         q -= 10;
  1098.     }
  1099. }
  1100. #endif
  1101.