home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / pine / pine3.07 / pico / display.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-03-30  |  44.8 KB  |  2,065 lines

  1. /*
  2.  * Program:    Display functions
  3.  *
  4.  * Modifier:    Michael Seibel
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: mikes@cac.washington.edu
  11.  *
  12.  * Date:    19 Jan 1991
  13.  * Last Edited:    6 Jan 1992
  14.  *
  15.  * Copyright 1991 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36. /*
  37.  * The functions in this file handle redisplay. There are two halves, the
  38.  * ones that update the virtual display screen, and the ones that make the
  39.  * physical display screen the same as the virtual display screen. These
  40.  * functions use hints that are left in the windows by the commands.
  41.  *
  42.  */
  43.  
  44. #include        <stdio.h>
  45. #include    "osdep.h"
  46. #include    "estruct.h"
  47. #include        "edef.h"
  48. #include        "pico.h"
  49.  
  50.  
  51. #define WFDEBUG 0                       /* Window flag debug. */
  52.  
  53. typedef struct  VIDEO {
  54.         short   v_flag;                 /* Flags */
  55.         char    v_text[1];              /* Screen data. */
  56. }       VIDEO;
  57.  
  58. #define VFCHG   0x0001                  /* Changed flag            */
  59. #define    VFEXT    0x0002            /* extended (beyond column 80)    */
  60. #define    VFREV    0x0004            /* reverse video status        */
  61. #define    VFREQ    0x0008            /* reverse video request    */
  62.  
  63. int     vtrow   = 0;                    /* Row location of SW cursor */
  64. int     vtcol   = 0;                    /* Column location of SW cursor */
  65. int     ttrow   = HUGE;                 /* Row location of HW cursor */
  66. int     ttcol   = HUGE;                 /* Column location of HW cursor */
  67. int    lbound    = 0;            /* leftmost column of current line
  68.                        being displayed */
  69.  
  70. VIDEO   **vscreen;                      /* Virtual screen. */
  71. VIDEO   **pscreen;                      /* Physical screen. */
  72.  
  73.  
  74. /*
  75.  * Initialize the data structures used by the display code. The edge vectors
  76.  * used to access the screens are set up. The operating system's terminal I/O
  77.  * channel is set up. All the other things get initialized at compile time.
  78.  * The original window has "WFCHG" set, so that it will get completely
  79.  * redrawn on the first call to "update".
  80.  */
  81. vtinit()
  82. {
  83.     register int i;
  84.     register VIDEO *vp;
  85.  
  86.     (*term.t_open)();
  87.  
  88.     (*term.t_rev)(FALSE);
  89.     vscreen = (VIDEO **) malloc((term.t_nrow+1)*sizeof(VIDEO *));
  90.     if (vscreen == NULL){
  91.     fprintf(stderr,"vtinit: vscreen: No Memory.\n");
  92.         exit(1);
  93.     }
  94.  
  95.  
  96.     pscreen = (VIDEO **) malloc((term.t_nrow+1)*sizeof(VIDEO *));
  97.     if (pscreen == NULL){
  98.     fprintf(stderr, "vtinit: pscreen: No Memory.\n");
  99.         exit(1);
  100.     }
  101.  
  102.  
  103.     for (i = 0; i <= term.t_nrow; ++i) {
  104.         vp = (VIDEO *) malloc(sizeof(VIDEO)+term.t_ncol);
  105.  
  106.         if (vp == NULL){
  107.         fprintf(stderr,"vtinit: vscreen[%d]: No Memory.\n", i);
  108.             exit(1);
  109.     }
  110.  
  111.     vp->v_flag = 0;
  112.         vscreen[i] = vp;
  113.         vp = (VIDEO *) malloc(sizeof(VIDEO)+term.t_ncol);
  114.  
  115.         if (vp == NULL){
  116.         fprintf(stderr,"vtinit: pscreen[%d]: No Memory.\n", i);
  117.             exit(1);
  118.     }
  119.  
  120.     vp->v_flag = 0;
  121.         pscreen[i] = vp;
  122.     }
  123.  
  124. }
  125.  
  126.  
  127. /*
  128.  * Clean up the virtual terminal system, in anticipation for a return to the
  129.  * operating system. Move down to the last line and clear it out (the next
  130.  * system prompt will be written in the line). Shut down the channel to the
  131.  * terminal.
  132.  */
  133. vttidy()
  134. {
  135.     movecursor(term.t_nrow-1, 0);
  136.     peeol();
  137.     movecursor(term.t_nrow, 0);
  138.     peeol();
  139.     (*term.t_close)();
  140. }
  141.  
  142.  
  143. /*
  144.  * Set the virtual cursor to the specified row and column on the virtual
  145.  * screen. There is no checking for nonsense values; this might be a good
  146.  * idea during the early stages.
  147.  */
  148. vtmove(row, col)
  149. {
  150.     vtrow = row;
  151.     vtcol = col;
  152. }
  153.  
  154.  
  155. /*
  156.  * Write a character to the virtual screen. The virtual row and column are
  157.  * updated. If the line is too long put a "$" in the last column. This routine
  158.  * only puts printing characters into the virtual terminal buffers. Only
  159.  * column overflow is checked.
  160.  */
  161. vtputc(c)
  162. int c;
  163. {
  164.     register VIDEO      *vp;
  165.  
  166.     vp = vscreen[vtrow];
  167.  
  168.     if (vtcol >= term.t_ncol) {
  169.         vtcol = (vtcol + 0x07) & ~0x07;
  170.         vp->v_text[term.t_ncol - 1] = '$';
  171.     }
  172.     else if (c == '\t') {
  173.         do {
  174.             vtputc(' ');
  175.     }
  176.         while ((vtcol&0x07) != 0);
  177.     }
  178.     else if (c < 0x20 || c == 0x7F) {
  179.         vtputc('^');
  180.         vtputc(c ^ 0x40);
  181.     }
  182.     else
  183.       vp->v_text[vtcol++] = c;
  184. }
  185.  
  186.  
  187. /*    put a character to the virtual screen in an extended line. If we are
  188.  *    not yet on left edge, don't print it yet. check for overflow on
  189.  *    the right margin.
  190.  */
  191. vtpute(c)
  192. int c;
  193. {
  194.     register VIDEO      *vp;
  195.  
  196.     vp = vscreen[vtrow];
  197.  
  198.     if (vtcol >= term.t_ncol) {
  199.         vtcol = (vtcol + 0x07) & ~0x07;
  200.         vp->v_text[term.t_ncol - 1] = '$';
  201.     }
  202.     else if (c == '\t'){
  203.         do {
  204.             vtpute(' ');
  205.         }
  206.         while (((vtcol + lbound)&0x07) != 0 && vtcol < term.t_ncol);
  207.     }
  208.     else if (c < 0x20 || c == 0x7F) {
  209.         vtpute('^');
  210.         vtpute(c ^ 0x40);
  211.     }
  212.     else {
  213.     if (vtcol >= 0)
  214.         vp->v_text[vtcol] = c;
  215.     ++vtcol;
  216.     }
  217. }
  218.  
  219.  
  220. /*
  221.  * Erase from the end of the software cursor to the end of the line on which
  222.  * the software cursor is located.
  223.  */
  224. vteeol()
  225. {
  226.     register VIDEO      *vp;
  227.  
  228.     vp = vscreen[vtrow];
  229.     while (vtcol < term.t_ncol)
  230.       vp->v_text[vtcol++] = ' ';
  231. }
  232.  
  233.  
  234. /*
  235.  * Make sure that the display is right. This is a three part process. First,
  236.  * scan through all of the windows looking for dirty ones. Check the framing,
  237.  * and refresh the screen. Second, make sure that "currow" and "curcol" are
  238.  * correct for the current window. Third, make the virtual and physical
  239.  * screens the same.
  240.  */
  241. update()
  242. {
  243.     register LINE *lp;
  244.     register WINDOW *wp;
  245.     register VIDEO *vp1;
  246.     register VIDEO *vp2;
  247.     register int i;
  248.     register int j;
  249.     register int c;
  250.     register int scroll = 0;
  251.  
  252. #if    TYPEAH
  253.     if (typahead())
  254.     return(TRUE);
  255. #endif
  256.  
  257.     if(Pmaster == NULL){
  258.     /* update the reverse video flags for any mode lines out there */
  259.     for (i = 0; i < term.t_nrow; ++i)
  260.       vscreen[i]->v_flag &= ~VFREQ;
  261.  
  262. #if    REVSTA
  263.     wp = wheadp;
  264.     while (wp != NULL) {
  265.         vscreen[0]->v_flag |= VFREQ;
  266.         wp = wp->w_wndp;
  267.     }
  268. #endif  /* REVSTA */
  269.     }
  270.  
  271.     wp = wheadp;
  272.  
  273.     while (wp != NULL){
  274.         /* Look at any window with update flags set on. */
  275.  
  276.         if (wp->w_flag != 0){
  277.             /* If not force reframe, check the framing. */
  278.  
  279.             if ((wp->w_flag & WFFORCE) == 0){
  280.                 lp = wp->w_linep;
  281.  
  282.                 for (i = 0; i < wp->w_ntrows; ++i){
  283.                     if (lp == wp->w_dotp)
  284.               goto out;
  285.  
  286.                     if (lp == wp->w_bufp->b_linep)
  287.               break;
  288.  
  289.                     lp = lforw(lp);
  290.         }
  291.         }
  292.  
  293.             /* Not acceptable, better compute a new value for the line at the
  294.              * top of the window. Then set the "WFHARD" flag to force full
  295.              * redraw.
  296.              */
  297.             i = wp->w_force;
  298.  
  299.             if (i > 0){
  300.                 --i;
  301.  
  302.                 if (i >= wp->w_ntrows)
  303.                   i = wp->w_ntrows-1;
  304.         }
  305.             else if (i < 0){
  306.                 i += wp->w_ntrows;
  307.  
  308.                 if (i < 0)
  309.           i = 0;
  310.         }
  311.             else if(optimize){
  312.         /* 
  313.          * find dotp, if its been moved just above or below the 
  314.          * window, use tcapscrollx() to facilitate quick redisplay...
  315.          */
  316.         lp = lforw(wp->w_dotp);
  317.         if(lp != wp->w_dotp){
  318.             if(lp == wp->w_linep && lp != wp->w_bufp->b_linep){
  319.             scroll = 1;
  320.             }
  321.             else {
  322.             lp = wp->w_linep;
  323.             for(j=0;j < wp->w_ntrows; ++j){
  324.                 if(lp != wp->w_bufp->b_linep)
  325.                   lp = lforw(lp);
  326.                 else
  327.                   break;
  328.             }
  329.             if(lp == wp->w_dotp && j == wp->w_ntrows)
  330.               scroll = 2;
  331.             }
  332.         }
  333.         j = i = wp->w_ntrows/2;
  334.         }
  335.         else
  336.           i = wp->w_ntrows/2;
  337.  
  338.             lp = wp->w_dotp;
  339.  
  340.             while (i != 0 && lback(lp) != wp->w_bufp->b_linep){
  341.                 --i;
  342.                 lp = lback(lp);
  343.         }
  344.  
  345.         /*
  346.          * this is supposed to speed things up by using tcap sequences
  347.          * to efficiently scroll the terminal screen.  the thinking here
  348.          * is that its much faster to update pscreen[] than to actually
  349.          * write the stuff to the screen...
  350.          */
  351.         if(optimize){
  352.         switch(scroll){
  353.           case 1:            /* scroll text down */
  354.             j = j-i+1;            /* add one for dot line */
  355.             /* 
  356.              * do we scroll down the header as well?  Well, only 
  357.              * if we're not editing the header, we've backed up 
  358.              * to the top, and the composer is not being 
  359.              * displayed...
  360.              */
  361.             if(Pmaster && !ComposerEditing 
  362.                && (lback(lp) == wp->w_bufp->b_linep)
  363.                && (ComposerTopLine == COMPOSER_TOP_LINE))
  364.               j += entry_line(LASTHDR, TRUE);
  365.  
  366.             scrolldown(wp, -1, j);
  367.             break;
  368.           case 2:            /* scroll text up */
  369.             j = wp->w_ntrows - (j-i);    /* we chose new top line! */
  370.             if(Pmaster && j){
  371.             /* 
  372.              * do we scroll down the header as well?  Well, only 
  373.              * if we're not editing the header, we've backed up 
  374.              * to the top, and the composer is not being 
  375.              * displayed...
  376.              */
  377.             if(!ComposerEditing 
  378.                && (ComposerTopLine != COMPOSER_TOP_LINE))
  379.               scrollup(wp, COMPOSER_TOP_LINE, 
  380.                    j+entry_line(LASTHDR, TRUE));
  381.             else
  382.               scrollup(wp, -1, j);
  383.             }
  384.             else
  385.               scrollup(wp, -1, j);
  386.             break;
  387.             default :
  388.               break;
  389.         }
  390.         }
  391.  
  392.             wp->w_linep = lp;
  393.             wp->w_flag |= WFHARD;       /* Force full. */
  394. out:
  395.         /*
  396.          * if the line at the top of the page is the top line
  397.          * in the body, show the header...
  398.          */
  399.         if(Pmaster && !ComposerEditing){
  400.         if(lback(wp->w_linep) == wp->w_bufp->b_linep){
  401.             if(ComposerTopLine == COMPOSER_TOP_LINE){
  402.             i = term.t_nrow - 4 - HeaderLen();
  403.             if(i > 0 && nlforw() >= i) {    /* room for header ? */
  404.                 if((i = nlforw()/2) == 0 && term.t_nrow&1)
  405.                   i = 1;
  406.                 while(wp->w_linep != wp->w_bufp->b_linep && i--)
  407.                   wp->w_linep = lforw(wp->w_linep);
  408.                 
  409.             }
  410.             else
  411.               ToggleHeader(1);
  412.             }
  413.         }
  414.         else{
  415.             if(ComposerTopLine != COMPOSER_TOP_LINE)
  416.               ToggleHeader(0);        /* hide it ! */
  417.         }
  418.         }
  419.  
  420.             /* Try to use reduced update. Mode line update has its own special
  421.              * flag. The fast update is used if the only thing to do is within
  422.              * the line editing.
  423.              */
  424.             lp = wp->w_linep;
  425.             i = wp->w_toprow;
  426.  
  427.             if ((wp->w_flag & ~WFMODE) == WFEDIT){
  428.                 while (lp != wp->w_dotp){
  429.                     ++i;
  430.                     lp = lforw(lp);
  431.         }
  432.  
  433.                 vscreen[i]->v_flag |= VFCHG;
  434.                 vtmove(i, 0);
  435.  
  436.                 for (j = 0; j < llength(lp); ++j)
  437.                     vtputc(lgetc(lp, j));
  438.  
  439.                 vteeol();
  440.         }
  441.         else if ((wp->w_flag & (WFEDIT | WFHARD)) != 0){
  442.                 while (i < wp->w_toprow+wp->w_ntrows){
  443.                     vscreen[i]->v_flag |= VFCHG;
  444.                     vtmove(i, 0);
  445.  
  446.             /* if line has been changed */
  447.                     if (lp != wp->w_bufp->b_linep){
  448.                         for (j = 0; j < llength(lp); ++j)
  449.                             vtputc(lgetc(lp, j));
  450.  
  451.                         lp = lforw(lp);
  452.             }
  453.  
  454.                     vteeol();
  455.                     ++i;
  456.         }
  457.         }
  458. #if ~WFDEBUG
  459.             if ((wp->w_flag&WFMODE) != 0)
  460.                 modeline(wp);
  461.  
  462.             wp->w_flag  = 0;
  463.             wp->w_force = 0;
  464. #endif
  465.     }
  466. #if WFDEBUG
  467.         modeline(wp);
  468.         wp->w_flag =  0;
  469.         wp->w_force = 0;
  470. #endif
  471.  
  472.     /* and onward to the next window */
  473.         wp = wp->w_wndp;
  474.     }
  475.  
  476.     /* Always recompute the row and column number of the hardware cursor. This
  477.      * is the only update for simple moves.
  478.      */
  479.     lp = curwp->w_linep;
  480.     currow = curwp->w_toprow;
  481.  
  482.     while (lp != curwp->w_dotp){
  483.         ++currow;
  484.         lp = lforw(lp);
  485.     }
  486.  
  487.     curcol = 0;
  488.     i = 0;
  489.  
  490.     while (i < curwp->w_doto){
  491.     c = lgetc(lp, i++);
  492.  
  493.         if (c == '\t')
  494.             curcol |= 0x07;
  495.         else if (c < 0x20 || c == 0x7F)
  496.             ++curcol;
  497.  
  498.         ++curcol;
  499.     }
  500.  
  501.     if (curcol >= term.t_ncol - 1) {         /* extended line. */
  502.     /* flag we are extended and changed */
  503.     vscreen[currow]->v_flag |= VFEXT | VFCHG;
  504.     updext();                /* and output extended line */
  505.     } else
  506.       lbound = 0;                /* not extended line */
  507.  
  508.     /* make sure no lines need to be de-extended because the cursor is
  509.      * no longer on them 
  510.      */
  511.  
  512.     wp = wheadp;
  513.  
  514.     while (wp != NULL) {
  515.     lp = wp->w_linep;
  516.     i = wp->w_toprow;
  517.  
  518.     while (i < wp->w_toprow + wp->w_ntrows) {
  519.         if (vscreen[i]->v_flag & VFEXT) {
  520.         /* always flag extended lines as changed */
  521.         vscreen[i]->v_flag |= VFCHG;
  522.         if ((wp != curwp) || (lp != wp->w_dotp) ||
  523.             (curcol < term.t_ncol - 1)) {
  524.             vtmove(i, 0);
  525.             for (j = 0; j < llength(lp); ++j)
  526.               vtputc(lgetc(lp, j));
  527.             vteeol();
  528.  
  529.             /* this line no longer is extended */
  530.             vscreen[i]->v_flag &= ~VFEXT;
  531.         }
  532.         }
  533.         lp = lforw(lp);
  534.         ++i;
  535.     }
  536.     /* and onward to the next window */
  537.         wp = wp->w_wndp;
  538.     }
  539.  
  540.     /* Special hacking if the screen is garbage. Clear the hardware screen,
  541.      * and update your copy to agree with it. Set all the virtual screen
  542.      * change bits, to force a full update.
  543.      */
  544.  
  545.     if (sgarbf != FALSE){
  546.     if(Pmaster){
  547.         showCompTitle();
  548.  
  549.         if(ComposerTopLine != COMPOSER_TOP_LINE){
  550.         UpdateHeader();            /* arrange things */
  551.         PaintHeader(COMPOSER_TOP_LINE, TRUE);
  552.         }
  553.  
  554.         /*
  555.          * since we're using only a portion of the screen and only 
  556.          * one buffer, only clear enough screen for the current window
  557.          * which is to say the *only* window.
  558.          */
  559.         for(i=wheadp->w_toprow;i<=term.t_nrow; i++){
  560.         movecursor(i, 0);
  561.         peeol();
  562.         vscreen[i]->v_flag |= VFCHG;
  563.         }
  564.         movecursor(wheadp->w_toprow, 0);
  565.     }
  566.     else{
  567.         for (i = 0; i < term.t_nrow - 2; ++i){
  568.  
  569.         vscreen[i]->v_flag |= VFCHG;
  570.         vp1 = pscreen[i];
  571.         for (j = 0; j < term.t_ncol; ++j)
  572.           vp1->v_text[j] = ' ';
  573.         }
  574.         movecursor(0, 0);                   /* Erase the screen. */
  575.         (*term.t_eeop)();
  576.  
  577.     }
  578.         sgarbf = FALSE;                /* Erase-page clears */
  579.         mpresf = FALSE;                /* the message area. */
  580.  
  581.     if(Pmaster)
  582.       modeline(curwp);
  583.     else
  584.       sgarbk = TRUE;            /* fix the keyhelp as well...*/
  585.     }
  586.  
  587.     /* Make sure that the physical and virtual displays agree. Unlike before,
  588.      * the "updateline" code is only called with a line that has been updated
  589.      * for sure.
  590.      */
  591.     if(Pmaster){
  592.     i = curwp->w_toprow;
  593.     c = term.t_nrow-2;
  594.     }
  595.     else{
  596.     i = 0;
  597.     c = term.t_nrow;
  598.     }
  599.  
  600.     for (; i < c; ++i){
  601.  
  602.         vp1 = vscreen[i];
  603.  
  604.     /* for each line that needs to be updated, or that needs its
  605.        reverse video status changed, call the line updater    */
  606.     j = vp1->v_flag;
  607.         if (((j & VFCHG) != 0) || (((j & VFREV) == 0) != ((j & VFREQ) == 0))){
  608.  
  609. #if    TYPEAH
  610.         if (typahead())
  611.             return(TRUE);
  612. #endif
  613.             vp2 = pscreen[i];
  614.  
  615.             updateline(i, &vp1->v_text[0], &vp2->v_text[0], &vp1->v_flag);
  616.  
  617.     }
  618.     }
  619.  
  620.     if(Pmaster == NULL){
  621.  
  622.     if(sgarbk != FALSE){
  623.         movecursor(term.t_nrow-1, 0);
  624.         peeol();
  625.         movecursor(term.t_nrow, 0);
  626.         peeol();
  627.         if(lastflag&CFFILL){
  628. #ifdef    SPELLER
  629.         wkeyhelp("GORYKCXJWVUT",
  630.                  "Get Help,WriteOut,Read File,Prev Pg,Del Line,Cur Pos,Exit,Justify,Where is,Next Pg,UnJustify,To Spell");
  631.  
  632. #else
  633.         wkeyhelp("GORYKCXJWVUD",
  634.                          "Get Help,WriteOut,Read File,Prev Pg,Del Line,Cur Pos,Exit,Justify,Where is,Next Pg,UnJustify,Del Char");
  635.  
  636. #endif
  637.         emlwrite("Can now UnJustify!", NULL);
  638.         mpresf = HUGE;    /* remove this after next keystroke! */
  639.         }
  640.         else{
  641. #ifdef SPELLER
  642.         wkeyhelp("GORYKCXJWVUT",
  643.                  "Get Help,WriteOut,Read File,Prev Pg,Del Line,Cur Pos,Exit,Justify,Where is,Next Pg,UnDel Line,To Spell");
  644. #else
  645.         wkeyhelp("GORYKCXJWVUD",
  646.                  "Get Help,WriteOut,Read File,Prev Pg,Del Line,Cur Pos,Exit,Justify,Where is,Next Pg,UnDel Line,Del Char");
  647.  
  648. #endif
  649.         }
  650.         sgarbk = FALSE;
  651.         }
  652.     }
  653.  
  654.     /* Finally, update the hardware cursor and flush out buffers. */
  655.  
  656.     movecursor(currow, curcol - lbound);
  657.     (*term.t_flush)();
  658. }
  659.  
  660. /* updext - update the extended line which the cursor is currently
  661.  *        on at a column greater than the terminal width. The line
  662.  *        will be scrolled right or left to let the user see where
  663.  *        the cursor is
  664.  */
  665. updext()
  666. {
  667.     register int rcursor;        /* real cursor location */
  668.     register LINE *lp;            /* pointer to current line */
  669.     register int j;            /* index into line */
  670.  
  671.     /* calculate what column the real cursor will end up in */
  672.     rcursor = ((curcol - term.t_ncol) % term.t_scrsiz) + term.t_margin;
  673.     lbound = curcol - rcursor + 1;
  674.  
  675.     /* scan through the line outputing characters to the virtual screen
  676.      * once we reach the left edge
  677.      */
  678.     vtmove(currow, -lbound);        /* start scanning offscreen */
  679.     lp = curwp->w_dotp;            /* line to output */
  680.     for (j=0; j<llength(lp); ++j)    /* until the end-of-line */
  681.       vtpute(lgetc(lp, j));
  682.  
  683.     /* truncate the virtual line */
  684.     vteeol();
  685.  
  686.     /* and put a '$' in column 1 */
  687.     vscreen[currow]->v_text[0] = '$';
  688. }
  689.  
  690.  
  691. /*
  692.  * Update a single line. This does not know how to use insert or delete
  693.  * character sequences; we are using VT52 functionality. Update the physical
  694.  * row and column variables. It does try an exploit erase to end of line. The
  695.  * RAINBOW version of this routine uses fast video.
  696.  */
  697. updateline(row, vline, pline, flags)
  698. char vline[];                /* what we want it to end up as */
  699. char pline[];                /* what it looks like now       */
  700. short *flags;                /* and how we want it that way  */
  701. {
  702.     register char *cp1;
  703.     register char *cp2;
  704.     register char *cp3;
  705.     register char *cp4;
  706.     register char *cp5;
  707.     register char *cp6;
  708.     register char *cp7;
  709.     register int  display = TRUE;
  710.     register int nbflag;        /* non-blanks to the right flag? */
  711.     int rev;                /* reverse video flag */
  712.     int req;                /* reverse video request flag */
  713.  
  714.  
  715.     /* set up pointers to virtual and physical lines */
  716.     cp1 = &vline[0];
  717.     cp2 = &pline[0];
  718.     cp3 = &vline[term.t_ncol];
  719.  
  720. #if    REVSTA
  721.     /* if we need to change the reverse video status of the
  722.      * current line, we need to re-write the entire line
  723.      */
  724.     rev = *flags & VFREV;
  725.     req = *flags & VFREQ;
  726.     if (rev != req) {
  727.     movecursor(row, 0);        /* Go to start of line. */
  728.     (*term.t_rev)(req != FALSE);    /* set rev video if needed */
  729.  
  730.     /* scan through the line and dump it to the screen and
  731.      *  the virtual screen array
  732.      */
  733.     while (cp1 < cp3) {
  734.         (*term.t_putchar)(*cp1);
  735.         ++ttcol;
  736.         *cp2++ = *cp1++;
  737.     }
  738.     (*term.t_rev)(FALSE);        /* turn rev video off */
  739.  
  740.     /* update the needed flags */
  741.     *flags &= ~VFCHG;
  742.     if (req)
  743.       *flags |= VFREV;
  744.     else
  745.       *flags &= ~VFREV;
  746.     return(TRUE);
  747.     }
  748. #endif
  749.  
  750.     /* advance past any common chars at the left */
  751.     while (cp1 != &vline[term.t_ncol] && cp1[0] == cp2[0]) {
  752.     ++cp1;
  753.     ++cp2;
  754.     }
  755.  
  756. /* This can still happen, even though we only call this routine on changed
  757.  * lines. A hard update is always done when a line splits, a massive
  758.  * change is done, or a buffer is displayed twice. This optimizes out most
  759.  * of the excess updating. A lot of computes are used, but these tend to
  760.  * be hard operations that do a lot of update, so I don't really care.
  761.  */
  762.     /* if both lines are the same, no update needs to be done */
  763.     if (cp1 == &vline[term.t_ncol]){
  764.     *flags &= ~VFCHG;        /* mark line clean! */
  765.     return(TRUE);
  766.     }
  767.  
  768.     /* find out if there is a match on the right */
  769.     nbflag = FALSE;
  770.     cp4 = &pline[term.t_ncol];
  771.  
  772.     while (cp3[-1] == cp4[-1]) {
  773.     --cp3;
  774.     --cp4;
  775.     if (cp3[0] != ' ')        /* Note if any nonblank */
  776.       nbflag = TRUE;        /* in right match. */
  777.     }
  778.  
  779.     cp5 = cp3;
  780.  
  781.     if (nbflag == FALSE && eolexist == TRUE) {    /* Erase to EOL ? */
  782.     while (cp5!=cp1 && cp5[-1]==' ')
  783.       --cp5;
  784.  
  785.     if (cp3-cp5 <= 3)        /* Use only if erase is */
  786.       cp5 = cp3;            /* fewer characters. */
  787.     }
  788.  
  789.     movecursor(row, cp1-&vline[0]);        /* Go to start of line. */
  790.  
  791.     if (!nbflag) {                /* use insert or del char? */
  792.     cp6 = cp3;
  793.     cp7 = cp4;
  794.  
  795.     if(inschar && (cp7!=cp2 && cp6[0]==cp7[-1])){
  796.         while (cp7!=cp2 && cp6[0]==cp7[-1]){
  797.         --cp7;
  798.         --cp6;
  799.         }
  800.  
  801.         if (cp7==cp2 && cp4-cp2 > 3){
  802.         o_insert(cp1[0]);     /* insert the char */
  803.         display = FALSE;        /* only do it once!! */
  804.         }
  805.     }
  806.     else if(delchar && cp3!=cp1 && cp7[0]==cp6[-1]){
  807.         while (cp6!=cp1 && cp7[0]==cp6[-1]){
  808.         --cp7;
  809.         --cp6;
  810.         }
  811.  
  812.         if (cp6==cp1 && cp5-cp6 > 3){
  813.         o_delete();        /* insert the char */
  814.         display = FALSE;        /* only do it once!! */
  815.         }
  816.     }
  817.     }
  818.  
  819.     while (cp1 != cp5) {        /* Ordinary. */
  820.     if(display)
  821.       (*term.t_putchar)(*cp1);
  822.     ++ttcol;
  823.     *cp2++ = *cp1++;
  824.     }
  825.  
  826.     if (cp5 != cp3) {            /* Erase. */
  827.     if(display)
  828.       peeol();
  829.     while (cp1 != cp3)
  830.       *cp2++ = *cp1++;
  831.     }
  832.     *flags &= ~VFCHG;            /* flag this line is changed */
  833. }
  834.  
  835.  
  836. /*
  837.  * Redisplay the mode line for the window pointed to by the "wp". This is the
  838.  * only routine that has any idea of how the modeline is formatted. You can
  839.  * change the modeline format by hacking at this routine. Called by "update"
  840.  * any time there is a dirty window.
  841.  */
  842. modeline(wp)
  843. WINDOW *wp;
  844. {
  845.     if(Pmaster){
  846.     char keys[13], labels[160];
  847.     
  848.         if(ComposerEditing)
  849.       ShowPrompt();
  850.     else{
  851.         sprintf(keys, "GCRYKOXJ%cVUT", 
  852.             (Pmaster->alt_ed != NULL) ? '_' : 'W');
  853.         sprintf(labels, "Get Help,Cancel,Read File,Prev Pg,Del Line,Postpone,Send,Justify,%s,Next Pg,%s,To Spell",  
  854.             (Pmaster->alt_ed) ? "Alt Edit" : "Where is",
  855.             (thisflag&CFFILL) ? "UnFill" : "UnDel Line");
  856.         wkeyhelp(keys, labels);
  857.     }
  858.  
  859.     return;
  860.     }
  861.     else{
  862.     register char *cp;
  863.     register int c;
  864.     register int n;        /* cursor position count */
  865.     register BUFFER *bp;
  866.     register i;        /* loop index */
  867.     register lchar;        /* character to draw line in buffer with */
  868.     register firstm;    /* is this the first mode? */
  869.     char     tline[NLINE];    /* buffer for part of mode line */
  870.  
  871.     n = 0;
  872. #if     MSDOS
  873.     vtmove(1, 0);
  874.     vteeol();
  875. #endif
  876.     vscreen[n]->v_flag |= VFCHG; /* Redraw next time. */
  877.     vtmove(n, 0);        /* Seek to right line. */
  878.  
  879. #if    REVSTA
  880.     if (revexist)
  881.       lchar = ' ';
  882.     else
  883. #endif
  884.       lchar = '-';
  885.  
  886.     vtputc(lchar);
  887.     bp = wp->w_bufp;
  888.  
  889.     n = 1;
  890.  
  891.     sprintf((cp=tline), "  PICO %s  ", version);    /* VERSION */
  892.     
  893.     while ((c = *cp++) != 0) {
  894.         vtputc(c);
  895.         ++n;
  896.     }
  897.  
  898.     if (bp->b_fname[0] != 0){            /* File name. */
  899.         sprintf(tline," File: ");
  900.         cp = &bp->b_fname[0];
  901.  
  902.         if(strlen(cp) > (term.t_ncol-n-22)){   /* room for full path ? */
  903.         strcat(tline,".../");
  904.         while(strlen(cp) > (term.t_ncol-n-22)){
  905.             if(strchr(cp,'/') == NULL)
  906.               cp = (char *)strchr(&bp->b_fname[0],'\0')-term.t_ncol-n-22;
  907.             else
  908.               cp = (char *)strchr(cp,'/') + 1;
  909.         }
  910.         }
  911.  
  912.         strcat(tline,cp);
  913.  
  914.         for(i=((term.t_ncol-n-12-strlen(tline))/2); i > 0; n++, i--)
  915.           vtputc(' ');
  916.  
  917.         cp = &tline[0];
  918.  
  919.         while ((c = *cp++) != 0){
  920.         vtputc(c);
  921.         ++n;
  922.             }
  923.  
  924.         }
  925.         else{
  926.         cp = " New Buffer ";
  927.             for(i=(term.t_ncol-strlen(cp))/2; n < i; n++)
  928.           vtputc(lchar);
  929.         while ((c = *cp++) != 0) {
  930.         vtputc(c);
  931.         ++n;
  932.             }
  933.     }
  934.  
  935. #if WFDEBUG
  936.     vtputc(lchar);
  937.     vtputc((wp->w_flag&WFMODE)!=0  ? 'M' : lchar);
  938.     vtputc((wp->w_flag&WFHARD)!=0  ? 'H' : lchar);
  939.     vtputc((wp->w_flag&WFEDIT)!=0  ? 'E' : lchar);
  940.     vtputc((wp->w_flag&WFMOVE)!=0  ? 'V' : lchar);
  941.     vtputc((wp->w_flag&WFFORCE)!=0 ? 'F' : lchar);
  942.     n += 6;
  943. #endif
  944.  
  945.     if ((bp->b_flag&BFCHG) != 0)                /* "MOD" if changed. */
  946.       cp = "Modified  ";
  947.     else
  948.       cp = "          ";
  949.  
  950.     while (n < (term.t_ncol - strlen(cp))){        /* Pad to full width. */
  951.         vtputc(lchar);
  952.         ++n;
  953.         }
  954.  
  955.     while ((c = *cp++) != 0){
  956.         vtputc(c);
  957.         ++n;
  958.     }
  959.     }
  960. }
  961.  
  962.  
  963.  
  964. /*
  965.  * Send a command to the terminal to move the hardware cursor to row "row"
  966.  * and column "col". The row and column arguments are origin 0. Optimize out
  967.  * random calls. Update "ttrow" and "ttcol".
  968.  */
  969. movecursor(row, col)
  970. {
  971.     if (row!=ttrow || col!=ttcol) {
  972.         ttrow = row;
  973.         ttcol = col;
  974.         (*term.t_move)(row, col);
  975.     }
  976. }
  977.  
  978.  
  979. /*
  980.  * Erase the message line. This is a special routine because the message line
  981.  * is not considered to be part of the virtual screen. It always works
  982.  * immediately; the terminal buffer is flushed via a call to the flusher.
  983.  */
  984. mlerase()
  985. {
  986.     int i;
  987.  
  988.     movecursor(term.t_nrow - 2, 0);
  989.     if (eolexist == TRUE)
  990.       peeol();
  991.     else {
  992.         for (i = 0; i < term.t_ncol - 1; i++)
  993.       (*term.t_putchar)(' ');
  994.         movecursor(term.t_nrow, 1);    /* force the move! */
  995.         movecursor(term.t_nrow, 0);
  996.     }
  997.     (*term.t_flush)();
  998.     mpresf = FALSE;
  999. }
  1000.  
  1001.  
  1002. /*
  1003.  * Ask a yes or no question in the message line. Return either TRUE, FALSE, or
  1004.  * ABORT. The ABORT status is returned if the user bumps out of the question
  1005.  * with a ^G. if d >= 0, d is the default answer returned. Otherwise there
  1006.  * is no default.
  1007.  */
  1008. mlyesno(prompt, dflt)
  1009. char  *prompt;
  1010. int   dflt;
  1011. {
  1012.     int  c;                /* input character */
  1013.  
  1014.     if(dflt >= 0)
  1015.       sprintf(s, "%s? [%c] : ", prompt, (dflt) ? 'y' : 'n');
  1016.     else
  1017.       sprintf(s, "%s (y/n)? ", prompt);
  1018.  
  1019.     while(1){
  1020.     mlwrite(s);
  1021.  
  1022.     (*term.t_rev)(1);
  1023.     while((c = GetKey()) == NODATA)
  1024.       ;                /* don't repaint if timeout */
  1025.     (*term.t_rev)(0);
  1026.  
  1027.     if(dflt >= 0 && c == (CTRL|'M')){
  1028.         (*term.t_rev)(1);
  1029.         pputs((dflt) ? "Yes" : "No");
  1030.         (*term.t_rev)(0);
  1031.         return(dflt);
  1032.     }
  1033.  
  1034.     if (c == (CTRL|'C') || c == F3){    /* Bail out! */
  1035.         (*term.t_rev)(1);
  1036.         pputs("ABORT");
  1037.         (*term.t_rev)(0);
  1038.         return(ABORT);
  1039.     }
  1040.  
  1041.     if (c=='y' || c=='Y'){
  1042.         (*term.t_rev)(1);
  1043.         pputs("Yes");
  1044.         (*term.t_rev)(0);
  1045.         return(TRUE);
  1046.     }
  1047.  
  1048.     if (c=='n' || c=='N'){
  1049.         (*term.t_rev)(1);
  1050.         pputs("No");
  1051.         (*term.t_rev)(0);
  1052.         return(FALSE);
  1053.     }
  1054.     else
  1055.       (*term.t_beep)();
  1056.     }
  1057. }
  1058.  
  1059.  
  1060.  
  1061. /*
  1062.  * Write a prompt into the message line, then read back a response. Keep
  1063.  * track of the physical position of the cursor. If we are in a keyboard
  1064.  * macro throw the prompt away, and return the remembered response. This
  1065.  * lets macros run at full speed. The reply is always terminated by a carriage
  1066.  * return. Handle erase, kill, and abort keys.
  1067.  */
  1068. mlreply(prompt, buf, nbuf, flg)
  1069. char  *prompt;
  1070. char  *buf;
  1071. short  flg;
  1072. {
  1073.     return(mlreplyd(prompt, buf, nbuf, flg|QDEFLT));
  1074. }
  1075.  
  1076.  
  1077. /*
  1078.  * function key mappings
  1079.  */
  1080. static int rfkm[12][2] = {
  1081.     { F1,  (CTRL|'G')},
  1082.     { F2,  0 },
  1083.     { F3,  (CTRL|'C')},
  1084.     { F4,  (CTRL|'T')},
  1085.     { F5,  0 },
  1086.     { F6,  0 },
  1087.     { F7,  0 },
  1088.     { F8,  0 },
  1089.     { F9,  0 },
  1090.     { F10, 0 },
  1091.     { F11, 0 },
  1092.     { F12, 0 }
  1093. };
  1094.  
  1095.  
  1096. /*
  1097.  * mlreplyd - write the prompt to the message line along with an default
  1098.  *          answer already typed in.  Carraige return accepts the
  1099.  *          default.  answer returned in buf which also holds the initial
  1100.  *            default, nbuf is it's length, def set means use default value,
  1101.  *            and ff means for-file which checks that all chars are allowed
  1102.  *            in file names.
  1103.  */
  1104. mlreplyd(prompt, buf, nbuf, flg)
  1105. char  *prompt;
  1106. char  *buf;
  1107. int    nbuf;
  1108. short  flg;
  1109. {
  1110.     register int    c;                /* current char       */
  1111.     register char   *b;                /* pointer in buf     */
  1112.     register int    i;
  1113.     register int    maxl;
  1114.     register int    plen;
  1115.     int      bc;
  1116.     int      changed = FALSE;
  1117.  
  1118.     mlwrite(prompt);
  1119.     plen = strlen(prompt);
  1120.     if(!(flg&QDEFLT))
  1121.       *buf = '\0';
  1122.  
  1123.     (*term.t_rev)(1);
  1124.  
  1125.     maxl = (nbuf < term.t_ncol - plen - 1) ? nbuf : term.t_ncol - plen - 1;
  1126.  
  1127.     pputs(buf);
  1128.     b = &buf[strlen(buf)];
  1129.     
  1130.     for (;;) {
  1131.     movecursor(ttrow, plen+b-buf);
  1132.     (*term.t_flush)();
  1133.  
  1134.  
  1135.     while((c = GetKey()) == NODATA)
  1136.       ;
  1137.  
  1138.     switch(normal(c, rfkm, 2)){
  1139.       case (CTRL|'A') :            /* CTRL-A beginning     */
  1140.         b = buf;
  1141.         continue;
  1142.  
  1143.       case (CTRL|'B') :            /* CTRL-B back a char   */
  1144.         if(ttcol > plen)
  1145.         b--;
  1146.         continue;
  1147.  
  1148.       case (CTRL|'C') :            /* CTRL-C abort        */
  1149.         pputs("^C");
  1150.         ctrlg(FALSE, 0);
  1151.         (*term.t_rev)(0);
  1152.         (*term.t_flush)();
  1153.         return(ABORT);
  1154.  
  1155.       case (CTRL|'E') :            /* CTRL-E end of line   */
  1156.         b = &buf[strlen(buf)];
  1157.         continue;
  1158.  
  1159.       case (CTRL|'F') :            /* CTRL-F forward a char*/
  1160.         if(*b != '\0')
  1161.         b++;
  1162.         continue;
  1163.  
  1164.       case (CTRL|'G') :            /* CTRL-G help        */
  1165.         pputs("HELP");
  1166.         (*term.t_rev)(0);
  1167.         (*term.t_flush)();
  1168.         return(HELPCH);
  1169.  
  1170.       case (CTRL|'H') :            /* CTRL-H backspace    */
  1171.       case 0x7f :                /*        rubout    */
  1172.         if (b <= buf)
  1173.           break;
  1174.         b--;
  1175.         ttcol--;                /* cheating!  no pputc */
  1176.         (*term.t_putchar)('\b');
  1177.  
  1178.       case (CTRL|'D') :            /* CTRL-D delete char   */
  1179.         changed=TRUE;
  1180.         i = 0;
  1181.         do                    /* blat out left char   */
  1182.           b[i] = b[i+1];
  1183.         while(b[i++] != '\0');
  1184.         break;
  1185.  
  1186.       case (CTRL|'L') :            /* CTRL-L redraw    */
  1187.         (*term.t_rev)(0);
  1188.         return(CTRL|'L');
  1189.  
  1190.       case (CTRL|'T') :            /* CTRL-T special    */
  1191.         (*term.t_rev)(0);
  1192.         return(CTRL|'T');
  1193.  
  1194.       case (CTRL|'K') :            /* CTRL-K kill line    */
  1195.         changed=TRUE;
  1196.         buf[0] = '\0';
  1197.         b = buf;
  1198.         movecursor(ttrow, plen);
  1199.         break;
  1200.  
  1201.       case K_PAD_LEFT:
  1202.         if(ttcol > plen)
  1203.           b--;
  1204.         continue;
  1205.  
  1206.       case K_PAD_RIGHT:
  1207.         if(*b != '\0')
  1208.           b++;
  1209.         continue;
  1210.  
  1211.       case F3 :                /* abort */
  1212.         pputs("ABORT");
  1213.         ctrlg(FALSE, 0);
  1214.         (*term.t_rev)(0);
  1215.         (*term.t_flush)();
  1216.         return(ABORT);
  1217.  
  1218.       case F1 :                /* sort of same thing */
  1219.         (*term.t_rev)(0);
  1220.         (*term.t_flush)();
  1221.         return(HELPCH);
  1222.  
  1223.       case (CTRL|'M') :            /*        newline       */
  1224.         (*term.t_rev)(0);
  1225.         (*term.t_flush)();
  1226.         return(changed);
  1227.  
  1228.       default : 
  1229.         if(strlen(buf) >= maxl){        /* contain the text      */
  1230.         (*term.t_beep)();
  1231.         continue;
  1232.         }
  1233.         changed=TRUE;
  1234.  
  1235.         if(c&(~0xff)){            /* bag ctrl/special chars */
  1236.         (*term.t_beep)();
  1237.         }
  1238.         else{
  1239.         i = strlen(b);
  1240.         if(flg&QFFILE){
  1241.             if(!fallowc(c)){         /* c OK in filename? */
  1242.             (*term.t_beep)();
  1243.             continue;
  1244.             }
  1245.         }
  1246.         do                /* blat out left char   */
  1247.           b[i+1] = b[i];
  1248.         while(i-- >= 0);
  1249.         *b++ = c;
  1250.         pputc(c);
  1251.         }
  1252.     }
  1253.  
  1254.     pputs(b);                /* show default */
  1255.     i = term.t_ncol-1;
  1256.     while(pscreen[ttrow]->v_text[i] == ' ')
  1257.       i--;
  1258.     while(ttcol <= i)
  1259.       pputc(' ');
  1260.     }
  1261. }
  1262.  
  1263.  
  1264. /*
  1265.  * emlwrite() - write the message string to the error half of the screen
  1266.  *              center justified.  much like mlwrite (which is still used
  1267.  *              to paint the line for prompts and such), except it center
  1268.  *              the text.
  1269.  */
  1270. emlwrite(message, arg) 
  1271. char    *message;
  1272. void    *arg;
  1273. {
  1274.     register char *bufp = message;
  1275.     register char *ap;
  1276.     register long l;
  1277.  
  1278.     mlerase();
  1279.  
  1280.     if((l = strlen(message)) == 0)        /* nothing to write, bag it */
  1281.       return;
  1282.  
  1283.     /*
  1284.      * next, figure out where the to move the cursor so the message 
  1285.      * comes out centered
  1286.      */
  1287.     if((ap=(char *)strchr(message, '%')) != NULL){
  1288.     l -= 2;
  1289.     switch(ap[1]){
  1290.       case '%':
  1291.       case 'c':
  1292.         l += 1;
  1293.         break;
  1294.       case 'd':
  1295.         l += (long)dumbroot((int)arg, 10);
  1296.         break;
  1297.       case 'D':
  1298.         l += (long)dumblroot((long)arg, 10);
  1299.         break;
  1300.       case 'o':
  1301.         l += (long)dumbroot((int)arg, 8);
  1302.         break;
  1303.       case 'x':
  1304.         l += (long)dumbroot((int)arg, 16);
  1305.         break;
  1306.       case 's':
  1307.             l += strlen((char *)arg);
  1308.         break;
  1309.     }
  1310.     }
  1311.  
  1312.     if(l-4 <= term.t_ncol)            /* this wouldn't be good */
  1313.       movecursor(term.t_nrow-2, (term.t_ncol - l - 4)/2);
  1314.     else
  1315.       movecursor(term.t_nrow-2, 0);
  1316.  
  1317.     (*term.t_rev)(1);
  1318.     pputs("[ ");
  1319.     while (*bufp != '\0' && ttcol < term.t_ncol-2){
  1320.     if(*bufp == '\007')
  1321.       (*term.t_beep)();
  1322.     else if(*bufp == '%'){
  1323.         switch(*++bufp){
  1324.           case 'c':
  1325.         pputc((char)arg);
  1326.         break;
  1327.           case 'd':
  1328.         mlputi((int)arg, 10);
  1329.         break;
  1330.           case 'D':
  1331.         mlputli((long)arg, 10);
  1332.         break;
  1333.           case 'o':
  1334.         mlputi((int)arg, 16);
  1335.         break;
  1336.           case 'x':
  1337.         mlputi((int)arg, 8);
  1338.         break;
  1339.           case 's':
  1340.         pputs((char *)arg);
  1341.         break;
  1342.           case '%':
  1343.           default:
  1344.         pputc(*bufp);
  1345.         break;
  1346.         }
  1347.     }
  1348.     else
  1349.       pputc(*bufp);
  1350.     bufp++;
  1351.     }
  1352.  
  1353.     pputs(" ]");
  1354.     (*term.t_rev)(0);
  1355.     (*term.t_flush)();
  1356.     mpresf = TRUE;
  1357. }
  1358.  
  1359.  
  1360. /*
  1361.  * Write a message into the message line. Keep track of the physical cursor
  1362.  * position. A small class of printf like format items is handled. Assumes the
  1363.  * stack grows down; this assumption is made by the "++" in the argument scan
  1364.  * loop. Set the "message line" flag TRUE.
  1365.  */
  1366. mlwrite(fmt, arg)
  1367. char *fmt;
  1368. void *arg;
  1369. {
  1370.     register int c;
  1371.     register char *ap;
  1372.  
  1373.     /*
  1374.      * the idea is to only highlight if there is something to show
  1375.      */
  1376.     mlerase();
  1377.  
  1378.     ttcol = 0;
  1379.     (*term.t_rev)(1);
  1380.     ap = (char *) &arg;
  1381.     while ((c = *fmt++) != 0) {
  1382.         if (c != '%') {
  1383.             (*term.t_putchar)(c);
  1384.             ++ttcol;
  1385.     }
  1386.         else {
  1387.             c = *fmt++;
  1388.             switch (c) {
  1389.           case 'd':
  1390.         mlputi(*(int *)ap, 10);
  1391.         ap += sizeof(int);
  1392.         break;
  1393.  
  1394.           case 'o':
  1395.         mlputi(*(int *)ap,  8);
  1396.         ap += sizeof(int);
  1397.         break;
  1398.  
  1399.           case 'x':
  1400.         mlputi(*(int *)ap, 16);
  1401.         ap += sizeof(int);
  1402.         break;
  1403.  
  1404.           case 'D':
  1405.         mlputli(*(long *)ap, 10);
  1406.         ap += sizeof(long);
  1407.         break;
  1408.  
  1409.           case 's':
  1410.         mlputs(*(char **)ap);
  1411.         ap += sizeof(char *);
  1412.         break;
  1413.  
  1414.               default:
  1415.         (*term.t_putchar)(c);
  1416.         ++ttcol;
  1417.         }
  1418.     }
  1419.     }
  1420.  
  1421.     c = ttcol;
  1422.     while(ttcol < term.t_ncol)
  1423.       pputc(' ');
  1424.  
  1425.     movecursor(term.t_nrow - 2, c);
  1426.     (*term.t_rev)(0);
  1427.     (*term.t_flush)();
  1428.     mpresf = TRUE;
  1429. }
  1430.  
  1431.  
  1432. /*
  1433.  * Write out a string. Update the physical cursor position. This assumes that
  1434.  * the characters in the string all have width "1"; if this is not the case
  1435.  * things will get screwed up a little.
  1436.  */
  1437. mlputs(s)
  1438. char *s;
  1439. {
  1440.     register int c;
  1441.  
  1442.     while ((c = *s++) != 0){
  1443.         (*term.t_putchar)(c);
  1444.         ++ttcol;
  1445.     }
  1446. }
  1447.  
  1448.  
  1449. /*
  1450.  * Write out an integer, in the specified radix. Update the physical cursor
  1451.  * position. This will not handle any negative numbers; maybe it should.
  1452.  */
  1453. mlputi(i, r)
  1454. {
  1455.     register int q;
  1456.     static char hexdigits[] = "0123456789ABCDEF";
  1457.  
  1458.     if (i < 0){
  1459.         i = -i;
  1460.         (*term.t_putchar)('-');
  1461.     }
  1462.  
  1463.     q = i/r;
  1464.  
  1465.     if (q != 0)
  1466.       mlputi(q, r);
  1467.  
  1468.     (*term.t_putchar)(hexdigits[i%r]);
  1469.     ++ttcol;
  1470. }
  1471.  
  1472.  
  1473. /*
  1474.  * do the same except as a long integer.
  1475.  */
  1476. mlputli(l, r)
  1477. long l;
  1478. {
  1479.     register long q;
  1480.  
  1481.     if (l < 0){
  1482.         l = -l;
  1483.         (*term.t_putchar)('-');
  1484.     }
  1485.  
  1486.     q = l/r;
  1487.  
  1488.     if (q != 0)
  1489.       mlputli(q, r);
  1490.  
  1491.     (*term.t_putchar)((int)(l%r)+'0');
  1492.     ++ttcol;
  1493. }
  1494.  
  1495.  
  1496. /*
  1497.  * scrolldown - use stuff to efficiently move blocks of text on the
  1498.  *              display, and update the pscreen array to reflect those
  1499.  *              moves...
  1500.  *
  1501.  *        wp is the window to move in
  1502.  *        r  is the row at which to begin scrolling
  1503.  *        n  is the number of lines to scrol
  1504.  */
  1505. scrolldown(wp, r, n)
  1506. WINDOW *wp;
  1507. {
  1508. #ifdef    TERMCAP
  1509.     register int i;
  1510.     register int l;
  1511.     register VIDEO *vp1;
  1512.     register VIDEO *vp2;
  1513.  
  1514.     if(!n)
  1515.       return;
  1516.  
  1517.     if(r < 0){
  1518.     r = wp->w_toprow;
  1519.     l = wp->w_ntrows;
  1520.     }
  1521.     else{
  1522.     if(r > wp->w_toprow)
  1523.         vscreen[r-1]->v_flag |= VFCHG;
  1524.     l = wp->w_toprow+wp->w_ntrows-r;
  1525.     }
  1526.  
  1527.     tcapscrolldn(r, n);
  1528.  
  1529.     for(i=l-n-1; i >=  0; i--){
  1530.     vp1 = pscreen[r+i]; 
  1531.     vp2 = pscreen[r+i+n];
  1532.     bcopy(vp1, vp2, term.t_ncol);
  1533.     }
  1534.     pprints(r+n-1, r, 1);
  1535.     ttrow = HUGE;
  1536.     ttcol = HUGE;
  1537. #endif /* TERMCAP */
  1538. }
  1539.  
  1540.  
  1541. /*
  1542.  * scrollup - use tcap stuff to efficiently move blocks of text on the
  1543.  *            display, and update the pscreen array to reflect those
  1544.  *            moves...
  1545.  */
  1546. scrollup(wp, r, n)
  1547. WINDOW *wp;
  1548. {
  1549. #ifdef    TERMCAP
  1550.     register int i,j;
  1551.     register VIDEO *vp1;
  1552.     register VIDEO *vp2;
  1553.  
  1554.     if(!n)
  1555.       return;
  1556.  
  1557.     if(r < 0)
  1558.       r = wp->w_toprow;
  1559.  
  1560.     tcapscrollup(r, n);
  1561.  
  1562.     i = 0;
  1563.     while(1){
  1564.     if(Pmaster){
  1565.         if(!(r+i+n < wp->w_toprow+wp->w_ntrows))
  1566.           break;
  1567.     }
  1568.     else{
  1569.         if(!((i < wp->w_ntrows-n)&&(r+i+n < wp->w_toprow+wp->w_ntrows)))
  1570.           break;
  1571.     }
  1572.     vp1 = pscreen[r+i+n]; 
  1573.     vp2 = pscreen[r+i];
  1574.     bcopy(vp1, vp2, term.t_ncol);
  1575.     i++;
  1576.     }
  1577.     pprints(wp->w_toprow+wp->w_ntrows-n, wp->w_toprow+wp->w_ntrows-1, 1);
  1578.     ttrow = HUGE;
  1579.     ttcol = HUGE;
  1580. #endif /* TERMCAP */
  1581. }
  1582.  
  1583.  
  1584. /*
  1585.  * print spaces in the physical screen starting from row abs(n) working in
  1586.  * either the positive or negative direction (depending on sign of n).
  1587.  */
  1588. pprints(x, y)
  1589. {
  1590.     register int i;
  1591.     register int j;
  1592.  
  1593.     if(x < y){
  1594.     for(i = x;i <= y; ++i){
  1595.       for(j = 0; j < term.t_ncol; j++)
  1596.         pscreen[i]->v_text[j] = ' ';
  1597.         }
  1598.     }
  1599.     else{
  1600.     for(i = x;i >= y; --i){
  1601.       for(j = 0; j < term.t_ncol; j++)
  1602.         pscreen[i]->v_text[j] = ' ';
  1603.         }
  1604.     }
  1605.     ttrow = y;
  1606.     ttcol = 0;
  1607. }
  1608.  
  1609.  
  1610.  
  1611. /*
  1612.  * doton - return the physical line number that the dot is on in the
  1613.  *         current window, and by side effect the number of lines remaining
  1614.  */
  1615. doton(r, chs)
  1616. int       *r;
  1617. unsigned  *chs;
  1618. {
  1619.     register int  i = 0;
  1620.     register LINE *lp = curwp->w_linep;
  1621.     int      l = -1;
  1622.  
  1623.     *chs = 0;
  1624.     while(i++ < curwp->w_ntrows){
  1625.     if(lp == curwp->w_dotp)
  1626.       l = i-1;
  1627.     lp = lforw(lp);
  1628.     if(lp == curwp->w_bufp->b_linep){
  1629.         i++;
  1630.         break;
  1631.     }
  1632.     if(l >= 0)
  1633.       (*chs) += llength(lp);
  1634.     }
  1635.     *r = i-l-2;
  1636.     return(l+curwp->w_toprow);
  1637. }
  1638.  
  1639.  
  1640.  
  1641. /*
  1642.  * resize_pico - given new window dimensions, allocate new resources
  1643.  */
  1644. resize_pico(row, col)
  1645. int  row, col;
  1646. {
  1647.     int old_nrow, old_ncol;
  1648.     register int i;
  1649.     register VIDEO *vp;
  1650.  
  1651.     old_nrow = term.t_nrow;
  1652.     old_ncol = term.t_ncol;
  1653.  
  1654.     term.t_nrow = row;
  1655.     term.t_ncol = col;
  1656.  
  1657.     if (old_ncol == term.t_ncol && old_nrow == term.t_nrow)
  1658.       return;
  1659.  
  1660.     curwp->w_toprow = 2;
  1661.     curwp->w_ntrows = term.t_nrow-4;           /* "-4" for mode line and keys */
  1662.  
  1663.     if(Pmaster)
  1664.       fillcol = (term.t_ncol > 80) ? 80 : term.t_ncol - 6;
  1665.     else
  1666.       fillcol = term.t_ncol - 6;           /* we control the fill column */
  1667.  
  1668.     /* 
  1669.      * free unused screen space ...
  1670.      */
  1671.     for(i=term.t_nrow+1; i <= old_nrow; ++i){
  1672.     free((char *) vscreen[i]);
  1673.     free((char *) pscreen[i]);
  1674.     }
  1675.  
  1676.     /* 
  1677.      * realloc new space for screen ...
  1678.      */
  1679.     if((vscreen=(VIDEO **)realloc(vscreen,(term.t_nrow+1)*sizeof(VIDEO *))) == NULL){
  1680.     if(Pmaster)
  1681.       return(-1);
  1682.     else
  1683.       exit(1);
  1684.     }
  1685.  
  1686.     if((pscreen=(VIDEO **)realloc(pscreen,(term.t_nrow+1)*sizeof(VIDEO *))) == NULL){
  1687.     if(Pmaster)
  1688.       return(-1);
  1689.     else
  1690.       exit(1);
  1691.     }
  1692.  
  1693.     for (i = 0; i <= term.t_nrow; ++i) {
  1694.     if(i <= old_nrow)
  1695.       vp = (VIDEO *) realloc(vscreen[i], sizeof(VIDEO)+term.t_ncol);
  1696.     else
  1697.       vp = (VIDEO *) malloc(sizeof(VIDEO)+term.t_ncol);
  1698.  
  1699.     if (vp == NULL)
  1700.       exit(1);
  1701.     vp->v_flag = VFCHG;
  1702.     vscreen[i] = vp;
  1703.     if(old_ncol < term.t_ncol){  /* don't let any garbage in */
  1704.         vtrow = i;
  1705.         vtcol = (i < old_nrow) ? old_ncol : 0;
  1706.         vteeol();
  1707.     }
  1708.  
  1709.     if(i <= old_nrow)
  1710.       vp = (VIDEO *) realloc(pscreen[i], sizeof(VIDEO)+term.t_ncol);
  1711.     else
  1712.       vp = (VIDEO *) malloc(sizeof(VIDEO)+term.t_ncol);
  1713.  
  1714.     if (vp == NULL)
  1715.       exit(1);
  1716.  
  1717.     vp->v_flag = VFCHG;
  1718.     pscreen[i] = vp;
  1719.     }
  1720.  
  1721.     if(!ResizeBrowser()){
  1722.     if(Pmaster){
  1723.         ResizeHeader();
  1724.     }
  1725.     else{
  1726.         lchange(WFHARD);                   /* set update flags... */
  1727.         curwp->w_flag |= WFMODE;           /* and modeline so we  */
  1728.         refresh(0, 1);                     /* redraw the whole enchilada. */
  1729.         update();                          /* do it */
  1730.     }
  1731.     }
  1732.  
  1733.     return(TRUE);
  1734. }
  1735.  
  1736.  
  1737. /*
  1738.  * showCompTitle - display the anchor line passed in from pine
  1739.  */
  1740. showCompTitle()
  1741. {
  1742.     if(Pmaster){
  1743.     register char *bufp = Pmaster->pine_anchor;
  1744.     movecursor(COMPOSER_TITLE_LINE, 0);
  1745.     (*term.t_rev)(1);   
  1746.     while (ttcol < term.t_ncol)
  1747.       if(*bufp != '\0')
  1748.         pputc(*bufp++, 1);
  1749.           else
  1750.         pputc(' ', 1);
  1751.  
  1752.     (*term.t_rev)(0);
  1753.     movecursor(COMPOSER_TITLE_LINE + 1, 0);
  1754.     peeol();
  1755.     }
  1756. }
  1757.  
  1758.  
  1759.  
  1760. /*
  1761.  * zotdisplay - blast malloc'd space created for display maps
  1762.  */
  1763. zotdisplay()
  1764. {
  1765.     register int i;
  1766.  
  1767.     for (i = 0; i <= term.t_nrow; ++i){        /* free screens */
  1768.     free((char *) vscreen[i]);
  1769.     free((char *) pscreen[i]);
  1770.     }
  1771.  
  1772.     free((char *) vscreen);
  1773.     free((char *) pscreen);
  1774. }
  1775.  
  1776.  
  1777.  
  1778. /*
  1779.  * nlforw() - returns the number of lines from the top to the dot
  1780.  */
  1781. nlforw()
  1782. {
  1783.     register int  i = 0;
  1784.     register LINE *lp = curwp->w_linep;
  1785.     
  1786.     while(lp != curwp->w_dotp){
  1787.     lp = lforw(lp);
  1788.     i++;
  1789.     }
  1790.     return(i);
  1791. }
  1792.  
  1793.  
  1794.  
  1795. /*
  1796.  * pputc - output the given char, keep track of it on the physical screen
  1797.  *       array, and keep track of the cursor
  1798.  */
  1799. pputc(c)
  1800. int c;
  1801. {
  1802.     if((ttcol >= 0 && ttcol < term.t_ncol) 
  1803.        && (ttrow >= 0 && ttrow <= term.t_nrow)){
  1804.     (*term.t_putchar)(c);            /* write it */
  1805.     pscreen[ttrow]->v_text[ttcol++] = c;    /* keep track of it */
  1806.     }
  1807. }
  1808.  
  1809.  
  1810. /*
  1811.  * pputs - print a string and keep track of the cursor
  1812.  */
  1813. pputs(s)
  1814. register char *s;
  1815. {
  1816.     while (*s != '\0')
  1817.       pputc(*s++);
  1818. }
  1819.  
  1820.  
  1821. /*
  1822.  * peeol - physical screen array erase to end of the line.  remember to
  1823.  *       track the cursor.
  1824.  */
  1825. peeol()
  1826. {
  1827.     register int r = ttrow;
  1828.     register int c = ttcol;
  1829.  
  1830.     (*term.t_eeol)();
  1831.     while(c < term.t_ncol && c >= 0 && r <= term.t_nrow && r >= 0)
  1832.       pscreen[r]->v_text[c++] = ' ';
  1833. }
  1834.  
  1835.  
  1836. /*
  1837.  * pscr - return the character on the physical screen map on the 
  1838.  *        given line, l, and offset, o.
  1839.  */
  1840. pscr(l, o)
  1841. {
  1842.     if((l >= 0 && l <= term.t_nrow) && (o >= 0 && o < term.t_ncol))
  1843.       return(pscreen[l]->v_text[o]);
  1844.     else
  1845.       return('\0');
  1846. }
  1847.  
  1848.  
  1849. /*
  1850.  * pclear() - clear the physical screen from row x through row y
  1851.  */
  1852. pclear(x, y)
  1853. register int x;
  1854. register int y;
  1855. {
  1856.     register int i;
  1857.  
  1858.     for(i=x; i < y; i++){
  1859.     movecursor(i, 0);
  1860.     peeol();
  1861.     }
  1862. }
  1863.  
  1864.  
  1865. /*
  1866.  * dumbroot - just get close 
  1867.  */
  1868. dumbroot(x, b)
  1869. int x, b;
  1870. {
  1871.     if(x < b)
  1872.       return(1);
  1873.     else
  1874.       return(dumbroot(x/b, b) + 1);
  1875. }
  1876.  
  1877.  
  1878. /*
  1879.  * dumblroot - just get close 
  1880.  */
  1881. dumblroot(x, b)
  1882. long x;
  1883. int  b;
  1884. {
  1885.     if(x < b)
  1886.       return(1);
  1887.     else
  1888.       return(dumblroot(x/b, b) + 1);
  1889. }
  1890.  
  1891.  
  1892. /*
  1893.  * pinsertc - use optimized insert, fixing physical screen map.
  1894.  *            returns true if char written, false otherwise
  1895.  */
  1896. pinsert(c)
  1897. int c;
  1898. {
  1899.     register int   i;
  1900.     register char *p;
  1901.  
  1902.     if(o_insert(c)){            /* if we've got it, use it! */
  1903.     p = pscreen[ttrow]->v_text;    /* then clean up physical screen */
  1904.     for(i = term.t_ncol-1; i > ttcol; i--)
  1905.       p[i] = p[i-1];        /* shift right */
  1906.     p[ttcol++] = c;            /* insert new char */
  1907.     
  1908.     return(1);
  1909.     }
  1910.  
  1911.     return(0);
  1912. }
  1913.  
  1914.  
  1915. /*
  1916.  * pdel - use optimized delete to rub out the current char and
  1917.  *        fix the physical screen array.
  1918.  *        returns true if optimized the delete, false otherwise
  1919.  */
  1920. pdel()
  1921. {
  1922.     register int   i;
  1923.     register char *c;
  1924.  
  1925.     if(delchar){            /* if we've got it, use it! */
  1926.     (*term.t_putchar)('\b');     /* move left a char */
  1927.     --ttcol;
  1928.     o_delete();            /* and delete it */
  1929.  
  1930.     c = pscreen[ttrow]->v_text;    /* then clean up physical screen */
  1931.     for(i=ttcol; i < term.t_ncol; i++)
  1932.       c[i] = c[i+1];
  1933.     c[i] = ' ';
  1934.     
  1935.     return(1);
  1936.     }
  1937.  
  1938.     return(0);
  1939. }
  1940.  
  1941.  
  1942.  
  1943. /*
  1944.  * wstripe - write out the given string at the given location, and reverse
  1945.  *           video on flagged characters.  Does the same thing as pine's
  1946.  *           stripe.
  1947.  */
  1948. wstripe(line, column, pmt, key)
  1949. int    line, column;
  1950. char    *pmt, key;
  1951. {
  1952.     register char *buf;
  1953.     register int  i = 0;
  1954.     register int  j = 0;
  1955.     register int  l;
  1956.  
  1957.     l = strlen(pmt);
  1958.     while(1){
  1959.     if(i >= term.t_ncol || j >= l)
  1960.       return;                /* equal strings */
  1961.  
  1962.     if(pmt[j] == key)
  1963.       j++;
  1964.  
  1965.     if(pscr(line, i) != pmt[j]){
  1966.         if(j >= 1 && pmt[j-1] == key)
  1967.           j--;
  1968.         break;
  1969.     }
  1970.  
  1971.     j++;
  1972.     i++;
  1973.     }
  1974.  
  1975.     movecursor(line, column+i);
  1976.     buf = &pmt[j];
  1977.     do{
  1978.     if(*buf == key){
  1979.         buf++;
  1980.         (*term.t_rev)(1);
  1981.         pputc(*buf);
  1982.         (*term.t_rev)(0);
  1983.     }
  1984.     else{
  1985.         pputc(*buf);
  1986.     }
  1987.     }    
  1988.     while(*++buf != '\0');
  1989.     peeol();
  1990.     (*term.t_flush)();
  1991. }
  1992.  
  1993.  
  1994.  
  1995. /*
  1996.  *  wkeyhelp - paint list of possible commands on the bottom
  1997.  *             of the display (yet another pine clone)
  1998.  */
  1999. wkeyhelp(keys, helptxt)
  2000. char    *keys;
  2001. char    *helptxt;
  2002. {
  2003.     char *obufp, *ibufp = helptxt, *kbufp = HelpKeyNames, *startp;
  2004.     int  i, j, spaces, index, copy;
  2005.  
  2006.     /*
  2007.      * make hardware cursor position change known!!
  2008.      */
  2009.     spaces = (term.t_ncol-3)/6;
  2010.     for(i=1;i>=0;i--){
  2011.     *s = '\0';
  2012.     obufp = &s[strlen(s)];
  2013.     for(j=1;j<=6;j++){
  2014.         index = j + (6*(1 - i));
  2015.         copy = keys[index-1] - '0';
  2016.         if(kbufp != NULL){
  2017.         do{
  2018.             if((*kbufp == '~') && (!copy))
  2019.               kbufp++;
  2020.             if(copy)
  2021.               *obufp++ = *kbufp++;
  2022.             else{
  2023.             *obufp++ = ' ';
  2024.             kbufp++;
  2025.             }
  2026.         }
  2027.         while((*kbufp != ',')? 1 : !(kbufp++));
  2028.         }
  2029.         else{
  2030.         if(copy){
  2031.             *obufp++ = '~';
  2032.             *obufp++ = '^';
  2033.             *obufp++ = '~';
  2034.             *obufp++ = copy + '0';
  2035.         }
  2036.         else{
  2037.             *obufp++ = ' ';
  2038.             *obufp++ = ' ';
  2039.         }
  2040.         }
  2041.  
  2042.         *obufp++ = ' ';
  2043.         startp = obufp;
  2044.         /*
  2045.          * we use "spaces-3" because of length key names
  2046.          */
  2047.         while(obufp - startp < (spaces-3)) {
  2048.         if((copy) && ((*ibufp != '\0') && (*ibufp != ',')))
  2049.           *obufp++ = *ibufp++;
  2050.         else
  2051.           *obufp++ = ' ';
  2052.         }
  2053.         if(copy){
  2054.         while((*ibufp != ',') && (*ibufp != '\0'))
  2055.           ibufp++;
  2056.         if(*ibufp != '\0')
  2057.           ibufp++;
  2058.         }
  2059.         *obufp = '\0';
  2060.     }
  2061.     wstripe(term.t_nrow-i,0,s,'~');
  2062.     }
  2063. }
  2064.  
  2065.