home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / NEXTSTEP / UNIX / Shells / zsh-3.0.5-MIHS / src / Src / zle_refresh.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-09-25  |  29.0 KB  |  1,095 lines

  1. /*
  2.  * $Id: zle_refresh.c,v 2.19 1996/10/15 20:16:35 hzoli Exp $
  3.  *
  4.  * zle_refresh.c - screen update
  5.  *
  6.  * This file is part of zsh, the Z shell.
  7.  *
  8.  * Copyright (c) 1992-1996 Paul Falstad
  9.  * All rights reserved.
  10.  *
  11.  * Permission is hereby granted, without written agreement and without
  12.  * license or royalty fees, to use, copy, modify, and distribute this
  13.  * software and to distribute modified versions of this software for any
  14.  * purpose, provided that the above copyright notice and the following
  15.  * two paragraphs appear in all copies of this software.
  16.  *
  17.  * In no event shall Paul Falstad or the Zsh Development Group be liable
  18.  * to any party for direct, indirect, special, incidental, or consequential
  19.  * damages arising out of the use of this software and its documentation,
  20.  * even if Paul Falstad and the Zsh Development Group have been advised of
  21.  * the possibility of such damage.
  22.  *
  23.  * Paul Falstad and the Zsh Development Group specifically disclaim any
  24.  * warranties, including, but not limited to, the implied warranties of
  25.  * merchantability and fitness for a particular purpose.  The software
  26.  * provided hereunder is on an "as is" basis, and Paul Falstad and the
  27.  * Zsh Development Group have no obligation to provide maintenance,
  28.  * support, updates, enhancements, or modifications.
  29.  *
  30.  */
  31.  
  32. #define ZLE
  33. #include "zsh.h"
  34.  
  35. #ifdef HAVE_SELECT
  36. #define SELECT_ADD_COST(X)    cost += X
  37. #define zputc(a, b)        putc(a, b), cost++
  38. #define zwrite(a, b, c, d)    fwrite(a, b, c, d), cost += (b * c)
  39. #else
  40. #define SELECT_ADD_COST(X)
  41. #define zputc(a, b)        putc(a, b)
  42. #define zwrite(a, b, c, d)    fwrite(a, b, c, d)
  43. #endif
  44.  
  45. /* Oct/Nov 94: <mason> some code savagely redesigned to fix several bugs -
  46.    refreshline() & tc_rightcurs() majorly rewritten; refresh() fixed -
  47.    I've put my fingers into just about every routine in here -
  48.    any queries about updates to mason@werple.net.au */
  49.  
  50. char **nbuf = NULL,        /* new video buffer line-by-line char array */
  51.     **obuf = NULL;        /* old video buffer line-by-line char array */
  52. static char *lpptbuf, *rpptbuf; /* prompt buffers                */
  53. static int more_start,        /* more text before start of screen?        */
  54.     more_end,            /* more stuff after end of screen?        */
  55.     olnct,            /* previous number of lines            */
  56.     ovln,            /* previous video cursor position line        */
  57.     lpptlen, rpptlen,           /* length of prompt buffers                 */
  58.     pptw, rpw,                  /* prompt widths on screen                  */
  59.     vcs, vln,            /* video cursor position column & line        */
  60.     vmaxln,            /* video maximum number of lines        */
  61.     winw, winh, rwinh,        /* window width & height            */
  62.     winpos;            /* singlelinezle: line's position in window */
  63. static unsigned pmpt_attr = 0,    /* text attributes after displaying prompt  */
  64.     rpmpt_attr = 0;        /* text attributes after displaying rprompt */
  65.  
  66. /**/
  67. void
  68. resetvideo(void)
  69. {
  70.     int ln;
  71.     static int lwinw = -1, lwinh = -1;    /* last window width & height */
  72.  
  73.     genprompts();
  74.     winw = columns;  /* terminal width */
  75.     if (termflags & TERM_SHORT)
  76.     winh = 1;
  77.     else
  78.     winh = (lines < 2) ? 24 : lines;
  79.     rwinh = lines;        /* keep the real number of lines */
  80.     winpos = vln = vmaxln = 0;
  81.     if (lwinw != winw || lwinh != winh) {
  82.     if (nbuf) {
  83.         for (ln = 0; ln != lwinh; ln++) {
  84.         zfree(nbuf[ln], lwinw + 2);
  85.         zfree(obuf[ln], lwinw + 2);
  86.         }
  87.         free(nbuf);
  88.         free(obuf);
  89.     }
  90.     nbuf = (char **)zcalloc((winh + 1) * sizeof(char *));
  91.     obuf = (char **)zcalloc((winh + 1) * sizeof(char *));
  92.     nbuf[0] = (char *)zalloc(winw + 2);
  93.     obuf[0] = (char *)zalloc(winw + 2);
  94.  
  95.     lwinw = winw;
  96.     lwinh = winh;
  97.     }
  98.     for (ln = 0; ln != winh + 1; ln++) {
  99.     if (nbuf[ln])
  100.         *nbuf[ln] = '\0';
  101.     if (obuf[ln])
  102.         *obuf[ln] = '\0';
  103.     }
  104.  
  105.     if (pptw) {
  106.         memset(nbuf[0], ' ', pptw);
  107.     memset(obuf[0], ' ', pptw);
  108.     nbuf[0][pptw] = obuf[0][pptw] = '\0';
  109.     }
  110.  
  111.     vcs = pptw;
  112.     olnct = nlnct = 0;
  113.     if (showinglist > 0)
  114.     showinglist = -2;
  115. }
  116.  
  117. /*
  118.  * Nov 96: <mason> changed to single line scroll
  119.  */
  120.  
  121. /**/
  122. void
  123. scrollwindow(int tline)
  124. {
  125.     int t0;
  126.     char *s;
  127.  
  128.     s = nbuf[tline];
  129.     for (t0 = tline; t0 < winh - 1; t0++)
  130.     nbuf[t0] = nbuf[t0 + 1];
  131.     nbuf[winh - 1] = s;
  132.     if (!tline)
  133.     more_start = 1;
  134.     return;
  135. }
  136.  
  137. /* this is the messy part. */
  138. /* this define belongs where it's used!!! */
  139.  
  140. #define nextline                    \
  141. {                            \
  142.     *s = '\0';                        \
  143.     if (ln != winh - 1)                    \
  144.     ln++;                        \
  145.     else {                        \
  146.     if (!canscroll)    {                \
  147.         if (nvln != -1 && nvln != winh - 1        \
  148.         && (numscrolls != onumscrolls - 1    \
  149.             || nvln <= winh / 2))        \
  150.             break;                    \
  151.         numscrolls++;                \
  152.         canscroll = winh / 2;            \
  153.     }                        \
  154.     canscroll--;                    \
  155.     scrollwindow(0);                \
  156.     if (nvln != -1)                    \
  157.         nvln--;                    \
  158.     }                            \
  159.     if (!nbuf[ln])                    \
  160.     nbuf[ln] = (char *)zalloc(winw + 2);        \
  161.     s = (unsigned char *)nbuf[ln];            \
  162.     sen = s + winw;                    \
  163. }
  164.  
  165. #define snextline                    \
  166. {                            \
  167.     *s = '\0';                        \
  168.     if (ln != winh - 1)                    \
  169.     ln++;                        \
  170.     else                        \
  171.     if (tosln < 3) {                \
  172.         more_status = 1;                \
  173.         scrollwindow(tosln + 1);            \
  174.     } else if (tosln - 1 <= nvln) {            \
  175.         scrollwindow(0);                \
  176.         if (nvln)                    \
  177.         nvln--, tosln--;            \
  178.     } else {                    \
  179.         tosln--;                    \
  180.         scrollwindow(tosln);            \
  181.     }                        \
  182.     if (!nbuf[ln])                    \
  183.     nbuf[ln] = (char *)zalloc(winw + 2);        \
  184.     s = (unsigned char *)nbuf[ln];            \
  185.     sen = s + winw;                    \
  186. }
  187.  
  188. #ifdef TIOCGWINSZ
  189. int winchanged;            /* window size changed */
  190. #endif
  191.  
  192. int cleareol,            /* clear to end-of-line (if can't cleareod) */
  193.     clearf = 0,            /* alwayslastprompt used immediately before */
  194.     hasam;            /* terminal should have auto-margin        */
  195. static int put_rpmpt,        /* whether we should display right-prompt   */
  196.     oput_rpmpt,            /* whether displayed right-prompt last time */
  197.     oxtabs,            /* oxtabs - tabs expand to spaces if set    */
  198.     numscrolls, onumscrolls;
  199. extern int clearflag;        /* set to non-zero if alwayslastprompt used */
  200.  
  201. /**/
  202. void
  203. refresh(void)
  204. {
  205.     static int inlist;        /* avoiding recursion                        */
  206.     int canscroll = 0,        /* number of lines we are allowed to scroll  */
  207.     ln = 0,            /* current line we're working on         */
  208.     more_status = 0,    /* more stuff in status line             */
  209.     nvcs = 0, nvln = -1,    /* video cursor column and line             */
  210.     t0 = -1,        /* tmp                         */
  211.     tosln = 0;        /* tmp in statusline stuff             */
  212.     unsigned char *s,        /* pointer into the video buffer         */
  213.     *t,            /* pointer into the real buffer             */
  214.     *sen,            /* pointer to end of the video buffer (eol)  */
  215.     *scs;            /* pointer to cursor position in real buffer */
  216.     char **qbuf;        /* tmp                         */
  217.  
  218.     /* If this is called from listmatches() (indirectly via trashzle()), and *
  219.      * that was called from the end of refresh(), then we don't need to do   *
  220.      * anything.  All this `inlist' code is actually unnecessary, but it     *
  221.      * improves speed a little in a common case.                             */
  222.     if (inlist)
  223.     return;
  224.  
  225. #ifdef HAVE_SELECT
  226.     cost = 0;            /* reset */
  227. #endif
  228.  
  229. /* Nov 96: <mason>  I haven't checked how complete this is.  sgtty stuff may
  230.    or may not work */
  231.     oxtabs = ((SGTTYFLAG & SGTABTYPE) == SGTABTYPE);
  232.  
  233.     cleareol = 0;        /* unset */
  234.     more_start = more_end = 0;    /* unset */
  235.     if (isset(SINGLELINEZLE) || lines < 3
  236.     || (termflags & (TERM_NOUP | TERM_BAD | TERM_UNKNOWN)))
  237.     termflags |= TERM_SHORT;
  238.     else
  239.     termflags &= ~TERM_SHORT;
  240.     if (resetneeded) {
  241.     onumscrolls = 0;
  242.     setterm();
  243. #ifdef TIOCGWINSZ
  244.     if (winchanged) {
  245.         moveto(0, 0);
  246.         t0 = olnct;        /* this is to clear extra lines even when */
  247.         winchanged = 0;    /* the terminal cannot TCCLEAREOD      */
  248.     }
  249. #endif
  250.     resetvideo();
  251.     resetneeded = 0;    /* unset */
  252.     oput_rpmpt = 0;        /* no right-prompt currently on screen */
  253.  
  254.     /* we probably should only have explicitly set attributes */
  255.     tsetcap(TCALLATTRSOFF, 0);
  256.     tsetcap(TCSTANDOUTEND, 0);
  257.     tsetcap(TCUNDERLINEEND, 0);
  258.  
  259.         if (!clearflag)
  260.             if (tccan(TCCLEAREOD))
  261.                 tcout(TCCLEAREOD);
  262.             else
  263.                 cleareol = 1;   /* request: clear to end of line */
  264.         if (t0 > -1)
  265.             olnct = t0;
  266.         if (termflags & TERM_SHORT)
  267.             vcs = 0;
  268.         else if (!clearflag && lpptlen)
  269.             zwrite(lpptbuf, lpptlen, 1, shout);
  270.     if (clearflag) {
  271.         zputc('\r', shout);
  272.         vcs = 0;
  273.         moveto(0, pptw);
  274.     }
  275.     fflush(shout);
  276.     clearf = clearflag;
  277.     } else if (winw != columns || rwinh != lines)
  278.     resetvideo();
  279.  
  280. /* now winw equals columns and winh equals lines 
  281.    width comparisons can be made with winw, height comparisons with winh */
  282.  
  283.     if (termflags & TERM_SHORT) {
  284.     singlerefresh();
  285.     return;
  286.     }
  287.  
  288.     if (cs < 0) {
  289. #ifdef DEBUG
  290.     fprintf(stderr, "BUG: negative cursor position\n");
  291.     fflush(stderr); 
  292. #endif
  293.     cs = 0;
  294.     }
  295.     scs = line + cs;
  296.     numscrolls = 0;
  297.  
  298. /* first, we generate the video line buffers so we know what to put on
  299.    the screen - also determine final cursor position (nvln, nvcs) */
  300.  
  301.     /* Deemed necessary by PWS 1995/05/15 due to kill-line problems */
  302.     if (!*nbuf)
  303.     *nbuf = (char *)zalloc(winw + 2);
  304.  
  305.     s = (unsigned char *)(nbuf[ln = 0] + pptw);
  306.     t = line;
  307.     sen = (unsigned char *)(*nbuf + winw);
  308.     for (; t < line+ll; t++) {
  309.     if (t == scs)            /* if cursor is here, remember it */
  310.         nvcs = s - (unsigned char *)(nbuf[nvln = ln]);
  311.  
  312.     if (*t == '\n')    {        /* newline */
  313.         nbuf[ln][winw + 1] = '\0';    /* text not wrapped */
  314.         nextline
  315.     } else if (*t == '\t') {        /* tab */
  316.         t0 = (char *)s - nbuf[ln];
  317.         if ((t0 | 7) + 1 >= winw) {
  318.         nbuf[ln][winw + 1] = '\n';    /* text wrapped */
  319.         nextline
  320.         } else
  321.         do
  322.             *s++ = ' ';
  323.         while ((++t0) & 7);
  324.     } else if (icntrl(*t)) {    /* other control character */
  325.         *s++ = '^';
  326.         if (s == sen) {
  327.         nbuf[ln][winw + 1] = '\n';    /* text wrapped */
  328.         nextline
  329.         }
  330.         *s++ = (*t == 127) ? '?' : (*t | '@');
  331.     } else                /* normal character */
  332.         *s++ = *t;
  333.     if (s == sen) {
  334.         nbuf[ln][winw + 1] = '\n';    /* text wrapped */
  335.         nextline
  336.     }
  337.     }
  338.  
  339. /* if we're really on the next line, don't fake it; do everything properly */
  340.     if (t == scs && (nvcs = s - (unsigned char *)(nbuf[nvln = ln])) == winw) {
  341.     nbuf[ln][winw + 1] = '\n';    /* text wrapped */
  342.     switch ('\0') {     /* a sad hack to make the break */
  343.     case '\0':        /* in nextline work */
  344.         nextline
  345.     }
  346.     *s = '\0';
  347.     nvcs = 0;
  348.     nvln++;
  349.     }
  350.  
  351.     if (t != line + ll)
  352.     more_end = 1;
  353.  
  354.     if (statusline) {
  355.     tosln = ln + 1;
  356.         if (ln == winh - 1) {
  357.         if (nvln > 0) {
  358.         scrollwindow(0);
  359.         nvln--;
  360.         }
  361.         tosln--;
  362.     }
  363.     nbuf[ln][winw + 1] = '\0';    /* text not wrapped */
  364.     snextline
  365.     t = (unsigned char *)statusline;
  366.     for (; t < (unsigned char *)statusline + statusll; t++) {
  367.         if (icntrl(*t)) {    /* simplified processing in the status line */
  368.         *s++ = '^';
  369.         if (s == sen) {
  370.             nbuf[ln][winw + 1] = '\n';    /* text wrapped */
  371.             snextline
  372.         }
  373.         *s++ = (*t == 127) ? '?' : (*t | '@');
  374.         } else
  375.         *s++ = *t;
  376.         if (s == sen) {
  377.         nbuf[ln][winw + 1] = '\n';    /* text wrapped */
  378.         snextline
  379.         }
  380.     }
  381.     }
  382.  
  383. /* insert <.... at end of last line if there is more text past end of screen */
  384.     if (more_end) {
  385.     if (!statusline)
  386.         tosln = winh;
  387.     strncpy(nbuf[tosln - 1] + winw - 7, " <.... ", 7);
  388.     nbuf[tosln - 1][winw] = nbuf[tosln - 1][winw + 1] = '\0';
  389.     }
  390.  
  391. /* insert <....> at end of first status line if status is too big */
  392.     if (more_status) {
  393.     strncpy(nbuf[tosln] + winw - 8, " <....> ", 8);
  394.     nbuf[tosln][winw] = nbuf[tosln][winw + 1] = '\0';
  395.     }
  396.  
  397.     *s = '\0';
  398.     nlnct = ln + 1;
  399.     for (ln = nlnct; ln < winh; ln++)
  400.     zfree(nbuf[ln], winw + 2), nbuf[ln] = NULL;
  401.  
  402. /* determine whether the right-prompt exists and can fit on the screen */
  403.     if (!more_start)
  404.     put_rpmpt = rpptlen && (int)strlen(nbuf[0]) + rpw < winw - 1;
  405.     else {
  406. /* insert >.... on first line if there is more text before start of screen */
  407.     memset(nbuf[0], ' ', pptw);
  408.     t0 = winw - pptw;
  409.     t0 = t0 > 5 ? 5 : t0;
  410.     strncpy(nbuf[0] + pptw, ">....", t0);
  411.     memset(nbuf[0] + pptw + t0, ' ', winw - t0 - pptw);
  412.     nbuf[0][winw] = nbuf[0][winw + 1] = '\0';
  413.     }
  414.  
  415.     for (ln = 0; !clearf && (ln < nlnct); ln++) {
  416.     /* if we have more lines than last time, clear the newly-used lines */
  417.     if (ln >= olnct)
  418.         cleareol = 1;
  419.  
  420.     /* if old line and new line are different,
  421.        see if we can insert/delete a line to speed up update */
  422.  
  423.     if (ln < olnct - 1 && !(hasam && vcs == winw) &&
  424.         nbuf[ln] && obuf[ln] &&
  425.         strncmp(nbuf[ln], obuf[ln], 16)) {
  426.         if (tccan(TCDELLINE) && obuf[ln + 1] && obuf[ln + 1][0] &&
  427.         nbuf[ln] && !strncmp(nbuf[ln], obuf[ln + 1], 16)) {
  428.         moveto(ln, 0);
  429.         tcout(TCDELLINE);
  430.         zfree(obuf[ln], winw + 2);
  431.         for (t0 = ln; t0 != olnct; t0++)
  432.             obuf[t0] = obuf[t0 + 1];
  433.         obuf[--olnct] = NULL;
  434.         }
  435.     /* don't try to insert a line if olnct = vmaxln (vmaxln is the number
  436.        of lines that have been displayed by this routine) so that we don't
  437.        go off the end of the screen. */
  438.  
  439.         else if (tccan(TCINSLINE) && olnct < vmaxln && nbuf[ln + 1] &&
  440.              obuf[ln] && !strncmp(nbuf[ln + 1], obuf[ln], 16)) {
  441.         moveto(ln, 0);
  442.         tcout(TCINSLINE);
  443.         for (t0 = olnct; t0 != ln; t0--)
  444.             obuf[t0] = obuf[t0 - 1];
  445.         obuf[ln] = NULL;
  446.         olnct++;
  447.         }
  448.     }
  449.  
  450.     /* update the single line */
  451.     refreshline(ln);
  452.  
  453.     /* output the right-prompt if appropriate */
  454.     if (put_rpmpt && !ln && !oput_rpmpt) {
  455.         moveto(0, winw - 1 - rpw);
  456.         zwrite(rpptbuf, rpptlen, 1, shout);
  457.         vcs = winw - 1;
  458.     /* reset character attributes to that set by the main prompt */
  459.         txtchange = pmpt_attr;
  460.         if (txtchangeisset(TXTNOBOLDFACE) && (rpmpt_attr & TXTBOLDFACE))
  461.         tsetcap(TCALLATTRSOFF, 0);
  462.         if (txtchangeisset(TXTNOSTANDOUT) && (rpmpt_attr & TXTSTANDOUT))
  463.         tsetcap(TCSTANDOUTEND, 0);
  464.         if (txtchangeisset(TXTNOUNDERLINE) && (rpmpt_attr & TXTUNDERLINE))
  465.         tsetcap(TCUNDERLINEEND, 0);
  466.         if (txtchangeisset(TXTBOLDFACE) && (rpmpt_attr & TXTNOBOLDFACE))
  467.         tsetcap(TCBOLDFACEBEG, 0);
  468.         if (txtchangeisset(TXTSTANDOUT) && (rpmpt_attr & TXTNOSTANDOUT))
  469.         tsetcap(TCSTANDOUTBEG, 0);
  470.         if (txtchangeisset(TXTUNDERLINE) && (rpmpt_attr & TXTNOUNDERLINE))
  471.         tsetcap(TCUNDERLINEBEG, 0);
  472.     }
  473.     }
  474.  
  475. /* if old buffer had extra lines, set them to be cleared and refresh them
  476. individually */
  477.  
  478.     if (olnct > nlnct) {
  479.     cleareol = 1;
  480.     for (ln = nlnct; ln < olnct; ln++)
  481.         refreshline(ln);
  482.     }
  483.  
  484. /* reset character attributes */
  485.     if (clearf && postedit) {
  486.     if ((txtchange = pmpt_attr ? pmpt_attr : rpmpt_attr)) {
  487.         if (txtchangeisset(TXTNOBOLDFACE))
  488.         tsetcap(TCALLATTRSOFF, 0);
  489.         if (txtchangeisset(TXTNOSTANDOUT))
  490.         tsetcap(TCSTANDOUTEND, 0);
  491.         if (txtchangeisset(TXTNOUNDERLINE))
  492.         tsetcap(TCUNDERLINEEND, 0);
  493.         if (txtchangeisset(TXTBOLDFACE))
  494.         tsetcap(TCBOLDFACEBEG, 0);
  495.         if (txtchangeisset(TXTSTANDOUT))
  496.         tsetcap(TCSTANDOUTBEG, 0);
  497.         if (txtchangeisset(TXTUNDERLINE))
  498.         tsetcap(TCUNDERLINEBEG, 0);
  499.     }
  500.     }
  501.     clearf = 0;
  502.  
  503. /* move to the new cursor position */
  504.     moveto(nvln, nvcs);
  505.  
  506. /* swap old and new buffers - better than freeing/allocating every time */
  507.     qbuf = nbuf;
  508.     nbuf = obuf;
  509.     obuf = qbuf;
  510. /* store current values so we can use them next time */
  511.     ovln = nvln;
  512.     olnct = nlnct;
  513.     oput_rpmpt = put_rpmpt;
  514.     onumscrolls = numscrolls;
  515.     if (nlnct > vmaxln)
  516.     vmaxln = nlnct;
  517.     fflush(shout);        /* make sure everything is written out */
  518.  
  519.     /* if we have a new list showing, note it; if part of the list has been
  520.     overwritten, redisplay it. */
  521.     if (showinglist == -2 || (showinglist > 0 && showinglist < nlnct)) {
  522.     inlist = 1;
  523.     listmatches();
  524.     inlist = 0;
  525.     refresh();
  526.     }
  527.     if (showinglist == -1)
  528.     showinglist = nlnct;
  529. }
  530.  
  531. #define tcinscost(X)   (tccan(TCMULTINS) ? tclen[TCMULTINS] : (X)*tclen[TCINS])
  532. #define tcdelcost(X)   (tccan(TCMULTDEL) ? tclen[TCMULTDEL] : (X)*tclen[TCDEL])
  533. #define tc_delchars(X)    (void) tcmultout(TCDEL, TCMULTDEL, (X))
  534. #define tc_inschars(X)    (void) tcmultout(TCINS, TCMULTINS, (X))
  535. #define tc_upcurs(X)    (void) tcmultout(TCUP, TCMULTUP, (X))
  536. #define tc_leftcurs(X)    (void) tcmultout(TCLEFT, TCMULTLEFT, (X))
  537.  
  538. /* refresh one line, using whatever speed-up tricks are provided by the tty */
  539.  
  540. /**/
  541. void
  542. refreshline(int ln)
  543. {
  544.     char *nl, *ol, *p1;        /* line buffer pointers             */
  545.     int ccs = 0,        /* temporary count for cursor position     */
  546.     char_ins = 0,        /* number of characters inserted/deleted */
  547.     col_cleareol,        /* clear to end-of-line from this column */
  548.     i, j,            /* tmp                     */
  549.     ins_last,        /* insert pushed last character off line */
  550.     nllen, ollen,        /* new and old line buffer lengths     */
  551.     rnllen;            /* real new line buffer length         */
  552.  
  553. /* 0: setup */
  554.     nl = nbuf[ln];
  555.     rnllen = nllen = nl ? strlen(nl) : 0;
  556.     ol = obuf[ln] ? obuf[ln] : "";
  557.     ollen = strlen(ol);
  558.  
  559. /* optimisation: can easily happen for clearing old lines.  If the terminal has
  560.    the capability, then this is the easiest way to skip unnecessary stuff */
  561.     if (cleareol && !nllen && !(hasam && ln < nlnct - 1)
  562.     && tccan(TCCLEAREOL)) {
  563.     moveto(ln, 0);
  564.     tcout(TCCLEAREOL);
  565.     return;    
  566.     }
  567.  
  568. /* 1: pad out the new buffer with spaces to contain _all_ of the characters
  569.       which need to be written. do this now to allow some pre-processing */
  570.  
  571.     if (cleareol         /* request to clear to end of line */
  572.     || !nllen         /* no line buffer given */
  573.     || (ln == 0 && (put_rpmpt != oput_rpmpt))) {    /* prompt changed */
  574.     p1 = halloc(winw + 2);
  575.     if (nllen)
  576.         strncpy(p1, nl, nllen);
  577.     memset(p1 + nllen, ' ', winw - nllen);
  578.     p1[winw] = '\0';
  579.     p1[winw + 1] = (nllen < winw) ? '\0' : nl[winw + 1];
  580.     if (ln && nbuf[ln])
  581.         memcpy(nl, p1, winw + 2);    /* next time obuf will be up-to-date */
  582.     else
  583.         nl = p1;        /* don't keep the padding for prompt line */
  584.     nllen = winw;
  585.     } else if (ollen > nllen) { /* make new line at least as long as old */
  586.     p1 = halloc(ollen + 1);
  587.     strncpy(p1, nl, nllen);
  588.     memset(p1 + nllen, ' ', ollen - nllen);
  589.     p1[ollen] = '\0';
  590.     nl = p1;
  591.     nllen = ollen;
  592.     }
  593.  
  594. /* 2: see if we can clear to end-of-line, and if it's faster, work out where
  595.    to do it from - we can normally only do so if there's no right-prompt.
  596.    With automatic margins, we shouldn't do it if there is another line, in
  597.    case it messes up cut and paste. */
  598.  
  599.     if (hasam && ln < nlnct - 1 && rnllen == winw)
  600.     col_cleareol = -2;    /* clearing eol would be evil so don't */
  601.     else {
  602.     col_cleareol = -1;
  603.     if (tccan(TCCLEAREOL) && (nllen == winw || put_rpmpt != oput_rpmpt)) {
  604.         for (i = nllen; i && nl[i - 1] == ' '; i--);
  605.         for (j = ollen; j && ol[j - 1] == ' '; j--);
  606.         if ((j > i + tclen[TCCLEAREOL])    /* new buf has enough spaces */
  607.         || (nllen == winw && nl[winw - 1] == ' '))
  608.         col_cleareol = i;
  609.     }
  610.     }
  611.  
  612. /* 2b: first a new trick for automargin niceness - good for cut and paste */
  613.  
  614.     if (hasam && vcs == winw) {
  615.     if (nbuf[vln] && nbuf[vln][vcs + 1] == '\n') {
  616.         vln++, vcs = 1;
  617.             if (nbuf[vln]  && *nbuf[vln])
  618.         zputc(*nbuf[vln], shout);
  619.         else
  620.         zputc(' ', shout);  /* I don't think this should happen */
  621.         if (ln == vln) {    /* better safe than sorry */
  622.         nl++;
  623.         if (*ol)
  624.             ol++;
  625.         ccs = 1;
  626.         }            /* else  hmmm... I wonder what happened */
  627.     } else {
  628.         vln++, vcs = 0;
  629.         zputc('\n', shout);
  630.     }
  631.     }
  632.     ins_last = 0;
  633.  
  634. /* 2c: if we're on the first line, start checking at the end of the prompt;
  635.    we shouldn't be doing anything within the prompt */
  636.  
  637.     if (ln == 0 && pptw) {
  638.     i = pptw - ccs;
  639.     j = strlen(ol);
  640.     nl += i;
  641.     ol += (i > j ? j : i);    /* if ol is too short, point it to '\0' */
  642.     ccs = pptw;
  643.     }
  644.  
  645. /* 3: main display loop - write out the buffer using whatever tricks we can */
  646.  
  647.     for (;;) {
  648.     if (*nl && *ol && nl[1] == ol[1]) /* skip only if second chars match */
  649.     /* skip past all matching characters */
  650.         for (; *nl && (*nl == *ol); nl++, ol++, ccs++) ;
  651.  
  652.     if (!*nl) {
  653.         if (ccs == winw && hasam && char_ins > 0 && ins_last
  654.         && vcs != winw) {
  655.         nl--;           /* we can assume we can go back here */
  656.         moveto(ln, winw - 1);
  657.         zputc(*nl, shout);
  658.         vcs++;
  659.         return;         /* write last character in line */
  660.         }
  661.         if ((char_ins <= 0) || (ccs >= winw))    /* written everything */
  662.         return;
  663.         if (tccan(TCCLEAREOL) && (char_ins >= tclen[TCCLEAREOL])
  664.             && col_cleareol != -2)
  665.         /* we've got junk on the right yet to clear */
  666.         col_cleareol = 0;    /* force a clear to end of line */
  667.     }
  668.  
  669.     moveto(ln, ccs);    /* move to where we do all output from */
  670.  
  671.     /* if we can finish quickly, do so */
  672.     if ((col_cleareol >= 0) && (ccs >= col_cleareol)) {
  673.         tcout(TCCLEAREOL);
  674.         return;
  675.     }
  676.  
  677.     /* we've written out the new but yet to clear rubbish due to inserts */
  678.     if (!*nl) {
  679.         i = (winw - ccs < char_ins) ? (winw - ccs) : char_ins;
  680.         if (tccan(TCDEL) && (tcdelcost(i) <= i + 1))
  681.         tc_delchars(i);
  682.         else {
  683.         vcs += i;
  684.         while (i-- > 0)
  685.             zputc(' ', shout);
  686.         }
  687.         return;
  688.     }
  689.  
  690.     /* if we've reached the end of the old buffer, then there are few tricks
  691.        we can do, so we just dump out what we must and clear if we can */
  692.     if (!*ol) {
  693.         i = (col_cleareol >= 0) ? col_cleareol : nllen;
  694.         i -= vcs;
  695.         zwrite(nl, i, 1, shout);
  696.         vcs += i;
  697.         if (col_cleareol >= 0)
  698.         tcout(TCCLEAREOL);
  699.         return;
  700.     }
  701.  
  702.     /* inserting & deleting chars: we can if there's no right-prompt */
  703.     if ((ln || !put_rpmpt || !oput_rpmpt) 
  704.         && (nl[1] && ol[1] && nl[1] != ol[1])) { 
  705.  
  706.     /* deleting characters - see if we can find a match series that
  707.        makes it cheaper to delete intermediate characters
  708.        eg. oldline: hifoobar \ hopefully cheaper here to delete two
  709.            newline: foobar     / characters, then we have six matches */
  710.         if (tccan(TCDEL)) {
  711.         for (i = 1; *(ol + i); i++)
  712.             if (tcdelcost(i) < pfxlen(ol + i, nl)) {
  713.             tc_delchars(i);
  714.             ol += i;
  715.             char_ins -= i;
  716.             i = 0;
  717.             break;
  718.             }
  719.         if (!i)
  720.             continue;
  721.         }
  722.     /* inserting characters - characters pushed off the right should be
  723.        annihilated, but we don't do this if we're on the last line lest
  724.        undesired scrolling occurs due to `illegal' characters on screen */
  725.  
  726.         if (tccan(TCINS) && (vln != lines - 1)) {    /* not on last line */
  727.         for (i = 1; *(nl + i); i++)
  728.             if (tcinscost(i) < pfxlen(nl + i, ol)) {
  729.             tc_inschars(i);
  730.             zwrite(nl, i, 1, shout);
  731.             nl += i;
  732.             char_ins += i;
  733.             ccs = (vcs += i);
  734.             /* if we've pushed off the right, truncate oldline */
  735.             for (i = 0; *(ol + i) && i < winw - ccs; i++);
  736.             if (i == winw - ccs) {
  737.                 *(ol + i) = '\0';
  738.                 ins_last = 1;
  739.             }
  740.             i = 0;
  741.             break;
  742.             }
  743.         if (!i)
  744.             continue;
  745.         }
  746.     }
  747.     /* we can't do any fancy tricks, so just dump the single character
  748.        and keep on trying */
  749.     zputc(*nl, shout);
  750.     nl++, ol++;
  751.     ccs++, vcs++;
  752.     }
  753. }
  754.  
  755. /* move the cursor to line ln (relative to the prompt line),
  756.    absolute column cl; update vln, vcs - video line and column */
  757.  
  758. /**/
  759. void
  760. moveto(int ln, int cl)
  761. {
  762.     int c;
  763.  
  764.     if (vcs == winw) {
  765.     vln++, vcs = 0;
  766.     if (!hasam) {
  767.         zputc('\r', shout);
  768.         zputc('\n', shout);
  769.     } else {
  770.         if ((vln < nlnct) && nbuf[vln] && *nbuf[vln])
  771.         c = *nbuf[vln];
  772.         else
  773.         c = ' ';
  774.         zputc(c, shout);
  775.         zputc('\r', shout);
  776.         if ((vln < olnct) && obuf[vln] && *obuf[vln])
  777.         *obuf[vln] = c;
  778.     }
  779.     }
  780.  
  781.     if (ln == vln && cl == vcs)
  782.     return;
  783.  
  784. /* move up */
  785.     if (ln < vln) {
  786.     tc_upcurs(vln - ln);
  787.     vln = ln;
  788.     }
  789. /* move down; if we might go off the end of the screen, use newlines
  790.    instead of TCDOWN */
  791.  
  792.     while (ln > vln) {
  793.     if (vln < vmaxln - 1)
  794.         if (ln > vmaxln - 1) {
  795.         if (tc_downcurs(vmaxln - 1 - vln))
  796.             vcs = 0;
  797.         vln = vmaxln - 1;
  798.         } else {
  799.         if (tc_downcurs(ln - vln))
  800.             vcs = 0;
  801.         vln = ln;
  802.         continue;
  803.         }
  804.     zputc('\r', shout), vcs = 0; /* safety precaution */
  805.     while (ln > vln) {
  806.         zputc('\n', shout);
  807.         vln++;
  808.     }
  809.     }
  810.  
  811.     if (cl == vcs)
  812.     return;
  813.  
  814. /* choose cheapest movements for ttys without multiple movement capabilities -
  815.    do this now because it's easier (to code) */
  816.     if (cl <= vcs / 2) {
  817.     zputc('\r', shout);
  818.     vcs = 0;
  819.     }
  820.     if (vcs < cl)
  821.     tc_rightcurs(cl);
  822.     else if (vcs > cl)
  823.     tc_leftcurs(vcs - cl);
  824.     vcs = cl;
  825. }
  826.  
  827. /**/
  828. int
  829. tcmultout(int cap, int multcap, int ct)
  830. {
  831.     if (tccan(multcap) && (!tccan(cap) || tclen[multcap] <= tclen[cap] * ct)) {
  832.     tcoutarg(multcap, ct);
  833.     return 1;
  834.     } else if (tccan(cap)) {
  835.     while (ct--)
  836.         tcout(cap);
  837.     return 1;
  838.     }
  839.     return 0;
  840. }
  841.  
  842. /**/
  843. void
  844. tc_rightcurs(int cl)
  845. {
  846.     int ct,            /* number of characters to move across        */
  847.     i = vcs,        /* cursor position after initial movements  */
  848.     j;
  849.     char *t;
  850.  
  851.     ct = cl - vcs;
  852.  
  853. /* do a multright if we can - it's the most reliable */
  854.     if (tccan(TCMULTRIGHT)) {
  855.     tcoutarg(TCMULTRIGHT, ct);
  856.     return;
  857.     }
  858.  
  859. /* try tabs if tabs are non destructive and multright is not possible */
  860.     if (!oxtabs && tccan(TCNEXTTAB) && ((vcs | 7) < cl)) {
  861.     i = (vcs | 7) + 1;
  862.     tcout(TCNEXTTAB);
  863.     for ( ; i + 8 <= cl; i += 8)
  864.         tcout(TCNEXTTAB);
  865.     if ((ct = cl - i) == 0) /* number of chars still to move across */
  866.         return;
  867.     }
  868.  
  869. /* otherwise _carefully_ write the contents of the video buffer.
  870.    if we're anywhere in the prompt, goto the left column and write the whole
  871.    prompt out unless lpptlen == pptw : we can cheat then */
  872.     if (vln == 0 && i < pptw) {
  873.     if (lpptlen == pptw)
  874.         zwrite(lpptbuf + i, lpptlen - i, 1, shout);
  875.     else if (tccan(TCRIGHT) && (tclen[TCRIGHT] * ct <= lpptlen))
  876.         /* it is cheaper to send TCRIGHT than reprint the whole prompt */
  877.         for (ct = pptw - i; ct--; )
  878.         tcout(TCRIGHT);
  879.         else {
  880.         if (i != 0)
  881.         zputc('\r', shout);
  882.         zwrite(lpptbuf, lpptlen, 1, shout);
  883.     }
  884.     i = pptw;
  885.     ct = cl - i;
  886.     }
  887.  
  888.     if (nbuf[vln]) {
  889.     for (j = 0, t = nbuf[vln]; *t && (j < i); j++, t++);
  890.     if (j == i)
  891.         for ( ; *t && ct; ct--, t++)
  892.         zputc(*t, shout);
  893.     }
  894.     while (ct--)
  895.     zputc(' ', shout);    /* not my fault your terminal can't go right */
  896. }
  897.  
  898. /**/
  899. int
  900. tc_downcurs(int ct)
  901. {
  902.     int ret = 0;
  903.  
  904.     if (ct && !tcmultout(TCDOWN, TCMULTDOWN, ct)) {
  905.     while (ct--)
  906.         zputc('\n', shout);
  907.     zputc('\r', shout), ret = -1;
  908.     }
  909.     return ret;
  910. }
  911.  
  912. /**/
  913. void
  914. tcout(int cap)
  915. {
  916.     tputs(tcstr[cap], 1, putshout);
  917.     SELECT_ADD_COST(tclen[cap]);
  918. }
  919.  
  920. /**/
  921. void
  922. tcoutarg(int cap, int arg)
  923. {
  924.     char *result;
  925.  
  926.     result = tgoto(tcstr[cap], arg, arg);
  927.     tputs(result, 1, putshout);
  928.     SELECT_ADD_COST(strlen(result));
  929. }
  930.  
  931. /**/
  932. void
  933. clearscreen(void)
  934. {
  935.     tcout(TCCLEARSCREEN);
  936.     resetneeded = 1;
  937.     clearflag = 0;
  938. }
  939.  
  940. /**/
  941. void
  942. redisplay(void)
  943. {
  944.     moveto(0, 0);
  945.     zputc('\r', shout);        /* extra care */
  946.     tc_upcurs(lppth - 1);
  947.     resetneeded = 1;
  948.     clearflag = 0;
  949. }
  950.  
  951. /**/
  952. void
  953. singlerefresh(void)
  954. {
  955.     char *vbuf, *vp,        /* video buffer and pointer    */
  956.     **qbuf,            /* tmp                   */
  957.     *refreshop = *obuf;    /* pointer to old video buffer */
  958.     int t0,            /* tmp                   */
  959.     vsiz,            /* size of new video buffer    */
  960.     nvcs = 0;        /* new video cursor column     */
  961.  
  962.     nlnct = 1;
  963. /* generate the new line buffer completely */
  964.     for (vsiz = 1 + pptw, t0 = 0; t0 != ll; t0++, vsiz++)
  965.     if (line[t0] == '\t')
  966.         vsiz = (vsiz | 7) + 1;
  967.     else if (icntrl(line[t0]))
  968.         vsiz++;
  969.     vbuf = (char *)zalloc(vsiz);
  970.  
  971.     if (cs < 0) {
  972. #ifdef DEBUG
  973.     fprintf(stderr, "BUG: negative cursor position\n");
  974.     fflush(stderr); 
  975. #endif
  976.     cs = 0;
  977.     }
  978.  
  979.     memcpy(vbuf, lpptbuf + lpptlen - pptw, pptw); /* only use last part of prompt */
  980.     vbuf[pptw] = '\0';
  981.     vp = vbuf + pptw;
  982.  
  983.     for (t0 = 0; t0 != ll; t0++) {
  984.     if (line[t0] == '\t')
  985.         for (*vp++ = ' '; (vp - vbuf) & 7; )
  986.         *vp++ = ' ';
  987.     else if (line[t0] == '\n') {
  988.         *vp++ = '\\';
  989.         *vp++ = 'n';
  990.     } else if (line[t0] == 0x7f) {
  991.         *vp++ = '^';
  992.         *vp++ = '?';
  993.     } else if (icntrl(line[t0])) {
  994.         *vp++ = '^';
  995.         *vp++ = line[t0] | '@';
  996.     } else
  997.         *vp++ = line[t0];
  998.     if (t0 == cs)
  999.         nvcs = vp - vbuf - 1;
  1000.     }
  1001.     if (t0 == cs)
  1002.     nvcs = vp - vbuf;
  1003.     *vp = '\0';
  1004.  
  1005. /* determine which part of the new line buffer we want for the display */
  1006.     if ((winpos && nvcs < winpos + 1) || (nvcs > winpos + winw - 2)) {
  1007.     if ((winpos = nvcs - ((winw - hasam) / 2)) < 0)
  1008.         winpos = 0;
  1009.     }
  1010.     if (winpos)
  1011.     vbuf[winpos] = '<';    /* line continues to the left */
  1012.     if ((int)strlen(vbuf + winpos) > (winw - hasam)) {
  1013.     vbuf[winpos + winw - hasam - 1] = '>';    /* line continues to right */
  1014.     vbuf[winpos + winw - hasam] = '\0';
  1015.     }
  1016.     strcpy(nbuf[0], vbuf + winpos);
  1017.     zfree(vbuf, vsiz);
  1018.     nvcs -= winpos;
  1019.  
  1020. /* display the `visable' portion of the line buffer */
  1021.     for (t0 = 0, vp = *nbuf;;) {
  1022.     /* skip past all matching characters */
  1023.     for (; *vp && *vp == *refreshop; t0++, vp++, refreshop++) ;
  1024.  
  1025.     if (!*vp && !*refreshop)
  1026.         break;
  1027.  
  1028.     singmoveto(t0);        /* move to where we do all output from */
  1029.  
  1030.     if (!*refreshop) {
  1031.         if ((t0 = strlen(vp)))
  1032.         zwrite(vp, t0, 1, shout);
  1033.         vcs += t0;
  1034.         break;
  1035.     }
  1036.     if (!*vp) {
  1037.         if (tccan(TCCLEAREOL))
  1038.         tcout(TCCLEAREOL);
  1039.         else
  1040.         for (; *refreshop++; vcs++)
  1041.             zputc(' ', shout);
  1042.         break;
  1043.     }
  1044.     zputc(*vp, shout);
  1045.     vcs++, t0++;
  1046.     vp++, refreshop++;
  1047.     }
  1048. /* move to the new cursor position */
  1049.     singmoveto(nvcs);
  1050.  
  1051.     qbuf = nbuf;
  1052.     nbuf = obuf;
  1053.     obuf = qbuf;
  1054.     fflush(shout);        /* make sure everything is written out */
  1055. }
  1056.  
  1057. /**/
  1058. void
  1059. singmoveto(int pos)
  1060. {
  1061.     if (pos == vcs)
  1062.     return;
  1063.     if (pos <= vcs / 2) {
  1064.     zputc('\r', shout);
  1065.     vcs = 0;
  1066.     }
  1067.     if (pos < vcs) {
  1068.     tc_leftcurs(vcs - pos);
  1069.     vcs = pos;
  1070.     }
  1071.     if (pos > vcs) {
  1072.     if (tcmultout(TCRIGHT, TCMULTRIGHT, pos - vcs))
  1073.         vcs = pos;
  1074.     else
  1075.         while (pos > vcs) {
  1076.         zputc(nbuf[0][vcs], shout);
  1077.         vcs++;
  1078.         }
  1079.     }
  1080. }
  1081.  
  1082. /* generate left and right prompts */
  1083.  
  1084. /**/
  1085. void
  1086. genprompts(void)
  1087. {
  1088.     zfree(lpptbuf, lpptlen);
  1089.     zfree(rpptbuf, rpptlen);
  1090.     lpptbuf = putprompt(lpmpt, &lpptlen, &pptw, 1);
  1091.     pmpt_attr = txtchange;
  1092.     rpptbuf = putprompt(rpmpt, &rpptlen, &rpw, 1);
  1093.     rpmpt_attr = txtchange;
  1094. }
  1095.