home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Spezial / SPEZIAL2_97.zip / SPEZIAL2_97.iso / ANWEND / EDITOR / NVI179B / NVI179B.ZIP / vi / vs_line.c < prev    next >
C/C++ Source or Header  |  1996-09-26  |  14KB  |  515 lines

  1. /*-
  2.  * Copyright (c) 1993, 1994
  3.  *    The Regents of the University of California.  All rights reserved.
  4.  * Copyright (c) 1992, 1993, 1994, 1995, 1996
  5.  *    Keith Bostic.  All rights reserved.
  6.  *
  7.  * See the LICENSE file for redistribution information.
  8.  */
  9.  
  10. #include "config.h"
  11.  
  12. #ifndef lint
  13. static const char sccsid[] = "@(#)vs_line.c    10.19 (Berkeley) 9/26/96";
  14. #endif /* not lint */
  15.  
  16. #include <sys/types.h>
  17. #include <sys/queue.h>
  18. #include <sys/time.h>
  19.  
  20. #include <bitstring.h>
  21. #include <limits.h>
  22. #include <stdio.h>
  23. #include <string.h>
  24.  
  25. #include "../common/common.h"
  26. #include "vi.h"
  27.  
  28. #ifdef VISIBLE_TAB_CHARS
  29. #define    TABCH    '-'
  30. #else
  31. #define    TABCH    ' '
  32. #endif
  33.  
  34. /*
  35.  * vs_line --
  36.  *    Update one line on the screen.
  37.  *
  38.  * PUBLIC: int vs_line __P((SCR *, SMAP *, size_t *, size_t *));
  39.  */
  40. int
  41. vs_line(sp, smp, yp, xp)
  42.     SCR *sp;
  43.     SMAP *smp;
  44.     size_t *xp, *yp;
  45. {
  46.     CHAR_T *kp;
  47.     GS *gp;
  48.     SMAP *tsmp;
  49.     size_t chlen, cno_cnt, cols_per_screen, len, nlen;
  50.     size_t offset_in_char, offset_in_line, oldx, oldy;
  51.     size_t scno, skip_cols, skip_screens;
  52.     int ch, dne, is_cached, is_partial, is_tab;
  53.     int list_tab, list_dollar;
  54.     char *p, *cbp, *ecbp, cbuf[128];
  55.  
  56. #if defined(DEBUG) && 0
  57.     TRACE(sp, "vs_line: row %u: line: %u off: %u\n",
  58.         smp - HMAP, smp->lno, smp->off);
  59. #endif
  60.     /*
  61.      * If ex modifies the screen after ex output is already on the screen,
  62.      * don't touch it -- we'll get scrolling wrong, at best.
  63.      */
  64.     if (!F_ISSET(sp, SC_TINPUT_INFO) && VIP(sp)->totalcount > 1)
  65.         return (0);
  66.     if (F_ISSET(sp, SC_SCR_EXWROTE) && smp - HMAP != LASTLINE(sp))
  67.         return (0);
  68.  
  69.     /*
  70.      * Assume that, if the cache entry for the line is filled in, the
  71.      * line is already on the screen, and all we need to do is return
  72.      * the cursor position.  If the calling routine doesn't need the
  73.      * cursor position, we can just return.
  74.      */
  75.     is_cached = SMAP_CACHE(smp);
  76.     if (yp == NULL && is_cached)
  77.         return (0);
  78.  
  79.     /*
  80.      * A nasty side effect of this routine is that it returns the screen
  81.      * position for the "current" character.  Not pretty, but this is the
  82.      * only routine that really knows what's out there.
  83.      *
  84.      * Move to the line.  This routine can be called by vs_sm_position(),
  85.      * which uses it to fill in the cache entry so it can figure out what
  86.      * the real contents of the screen are.  Because of this, we have to
  87.      * return to whereever we started from.
  88.      */
  89.     gp = sp->gp;
  90.     (void)gp->scr_cursor(sp, &oldy, &oldx);
  91.     (void)gp->scr_move(sp, smp - HMAP, 0);
  92.  
  93.     /* Get the line. */
  94.     dne = db_get(sp, smp->lno, 0, &p, &len);
  95.  
  96.     /*
  97.      * Special case if we're printing the info/mode line.  Skip printing
  98.      * the leading number, as well as other minor setup.  The only time
  99.      * this code paints the mode line is when the user is entering text
  100.      * for a ":" command, so we can put the code here instead of dealing
  101.      * with the empty line logic below.  This is a kludge, but it's pretty
  102.      * much confined to this module.
  103.      *
  104.      * Set the number of columns for this screen.
  105.      * Set the number of chars or screens to skip until a character is to
  106.      * be displayed.
  107.      */
  108.     cols_per_screen = sp->cols;
  109.     if (O_ISSET(sp, O_LEFTRIGHT)) {
  110.         skip_screens = 0;
  111.         skip_cols = smp->coff;
  112.     } else {
  113.         skip_screens = smp->soff - 1;
  114.         skip_cols = skip_screens * cols_per_screen;
  115.     }
  116.  
  117.     list_tab = O_ISSET(sp, O_LIST);
  118.     if (F_ISSET(sp, SC_TINPUT_INFO))
  119.         list_dollar = 0;
  120.     else {
  121.         list_dollar = list_tab;
  122.  
  123.         /*
  124.          * If O_NUMBER is set, the line doesn't exist and it's line
  125.          * number 1, i.e., an empty file, display the line number.
  126.          *
  127.          * If O_NUMBER is set, the line exists and the first character
  128.          * on the screen is the first character in the line, display
  129.          * the line number.
  130.          *
  131.          * !!!
  132.          * If O_NUMBER set, decrement the number of columns in the
  133.          * first screen.  DO NOT CHANGE THIS -- IT'S RIGHT!  The
  134.          * rest of the code expects this to reflect the number of
  135.          * columns in the first screen, regardless of the number of
  136.          * columns we're going to skip.
  137.          */
  138.         if (O_ISSET(sp, O_NUMBER)) {
  139.             cols_per_screen -= O_NUMBER_LENGTH;
  140.             if ((!dne || smp->lno == 1) && skip_cols == 0) {
  141.                 nlen = snprintf(cbuf,
  142.                     sizeof(cbuf), O_NUMBER_FMT, smp->lno);
  143.                 (void)gp->scr_addstr(sp, cbuf, nlen);
  144.             }
  145.         }
  146.     }
  147.  
  148.     /*
  149.      * Special case non-existent lines and the first line of an empty
  150.      * file.  In both cases, the cursor position is 0, but corrected
  151.      * as necessary for the O_NUMBER field, if it was displayed.
  152.      */
  153.     if (dne || len == 0) {
  154.         /* Fill in the cursor. */
  155.         if (yp != NULL && smp->lno == sp->lno) {
  156.             *yp = smp - HMAP;
  157.             *xp = sp->cols - cols_per_screen;
  158.         }
  159.  
  160.         /* If the line is on the screen, quit. */
  161.         if (is_cached)
  162.             goto ret1;
  163.  
  164.         /* Set line cache information. */
  165.         smp->c_sboff = smp->c_eboff = 0;
  166.         smp->c_scoff = smp->c_eclen = 0;
  167.  
  168.         /*
  169.          * Lots of special cases for empty lines, but they only apply
  170.          * if we're displaying the first screen of the line.
  171.          */
  172.         if (skip_cols == 0)
  173.             if (dne) {
  174.                 if (smp->lno == 1) {
  175.                     if (list_dollar) {
  176.                         ch = '$';
  177.                         goto empty;
  178.                     }
  179.                 } else {
  180.                     ch = '~';
  181.                     goto empty;
  182.                 }
  183.             } else
  184.                 if (list_dollar) {
  185.                     ch = '$';
  186. empty:                    (void)gp->scr_addstr(sp,
  187.                         KEY_NAME(sp, ch), KEY_LEN(sp, ch));
  188.                 }
  189.  
  190.         (void)gp->scr_clrtoeol(sp);
  191.         (void)gp->scr_move(sp, oldy, oldx);
  192.         return (0);
  193.     }
  194.  
  195.     /*
  196.      * If we just wrote this or a previous line, we cached the starting
  197.      * and ending positions of that line.  The way it works is we keep
  198.      * information about the lines displayed in the SMAP.  If we're
  199.      * painting the screen in the forward direction, this saves us from
  200.      * reformatting the physical line for every line on the screen.  This
  201.      * wins big on binary files with 10K lines.
  202.      *
  203.      * Test for the first screen of the line, then the current screen line,
  204.      * then the line behind us, then do the hard work.  Note, it doesn't
  205.      * do us any good to have a line in front of us -- it would be really
  206.      * hard to try and figure out tabs in the reverse direction, i.e. how
  207.      * many spaces a tab takes up in the reverse direction depends on
  208.      * what characters preceded it.
  209.      *
  210.      * Test for the first screen of the line.
  211.      */
  212.     if (skip_cols == 0) {
  213.         smp->c_sboff = offset_in_line = 0;
  214.         smp->c_scoff = offset_in_char = 0;
  215.         p = &p[offset_in_line];
  216.         goto display;
  217.     }
  218.  
  219.     /* Test to see if we've seen this exact line before. */
  220.     if (is_cached) {
  221.         offset_in_line = smp->c_sboff;
  222.         offset_in_char = smp->c_scoff;
  223.         p = &p[offset_in_line];
  224.  
  225.         /* Set cols_per_screen to 2nd and later line length. */
  226.         if (O_ISSET(sp, O_LEFTRIGHT) || skip_cols > cols_per_screen)
  227.             cols_per_screen = sp->cols;
  228.         goto display;
  229.     }
  230.  
  231.     /* Test to see if we saw an earlier part of this line before. */
  232.     if (smp != HMAP &&
  233.         SMAP_CACHE(tsmp = smp - 1) && tsmp->lno == smp->lno) {
  234.         if (tsmp->c_eclen != tsmp->c_ecsize) {
  235.             offset_in_line = tsmp->c_eboff;
  236.             offset_in_char = tsmp->c_eclen;
  237.         } else {
  238.             offset_in_line = tsmp->c_eboff + 1;
  239.             offset_in_char = 0;
  240.         }
  241.  
  242.         /* Put starting info for this line in the cache. */
  243.         smp->c_sboff = offset_in_line;
  244.         smp->c_scoff = offset_in_char;
  245.         p = &p[offset_in_line];
  246.  
  247.         /* Set cols_per_screen to 2nd and later line length. */
  248.         if (O_ISSET(sp, O_LEFTRIGHT) || skip_cols > cols_per_screen)
  249.             cols_per_screen = sp->cols;
  250.         goto display;
  251.     }
  252.  
  253.     scno = 0;
  254.     offset_in_line = 0;
  255.     offset_in_char = 0;
  256.  
  257.     /* Do it the hard way, for leftright scrolling screens. */
  258.     if (O_ISSET(sp, O_LEFTRIGHT)) {
  259.         for (; offset_in_line < len; ++offset_in_line) {
  260.             chlen = (ch = *(u_char *)p++) == '\t' && !list_tab ?
  261.                 TAB_OFF(scno) : KEY_LEN(sp, ch);
  262.             if ((scno += chlen) >= skip_cols)
  263.                 break;
  264.         }
  265.  
  266.         /* Set cols_per_screen to 2nd and later line length. */
  267.         cols_per_screen = sp->cols;
  268.  
  269.         /* Put starting info for this line in the cache. */
  270.         if (scno != skip_cols) {
  271.             smp->c_sboff = offset_in_line;
  272.             smp->c_scoff =
  273.                 offset_in_char = chlen - (scno - skip_cols);
  274.             --p;
  275.         } else {
  276.             smp->c_sboff = ++offset_in_line;
  277.             smp->c_scoff = 0;
  278.         }
  279.     }
  280.  
  281.     /* Do it the hard way, for historic line-folding screens. */
  282.     else {
  283.         for (; offset_in_line < len; ++offset_in_line) {
  284.             chlen = (ch = *(u_char *)p++) == '\t' && !list_tab ?
  285.                 TAB_OFF(scno) : KEY_LEN(sp, ch);
  286.             if ((scno += chlen) < cols_per_screen)
  287.                 continue;
  288.             scno -= cols_per_screen;
  289.  
  290.             /* Set cols_per_screen to 2nd and later line length. */
  291.             cols_per_screen = sp->cols;
  292.  
  293.             /*
  294.              * If crossed the last skipped screen boundary, start
  295.              * displaying the characters.
  296.              */
  297.             if (--skip_screens == 0)
  298.                 break;
  299.         }
  300.  
  301.         /* Put starting info for this line in the cache. */
  302.         if (scno != 0) {
  303.             smp->c_sboff = offset_in_line;
  304.             smp->c_scoff = offset_in_char = chlen - scno;
  305.             --p;
  306.         } else {
  307.             smp->c_sboff = ++offset_in_line;
  308.             smp->c_scoff = 0;
  309.         }
  310.     }
  311.  
  312. display:
  313.     /*
  314.      * Set the number of characters to skip before reaching the cursor
  315.      * character.  Offset by 1 and use 0 as a flag value.  Vs_line is
  316.      * called repeatedly with a valid pointer to a cursor position.
  317.      * Don't fill anything in unless it's the right line and the right
  318.      * character, and the right part of the character...
  319.      */
  320.     if (yp == NULL ||
  321.         smp->lno != sp->lno || sp->cno < offset_in_line ||
  322.         offset_in_line + cols_per_screen < sp->cno) {
  323.         cno_cnt = 0;
  324.         /* If the line is on the screen, quit. */
  325.         if (is_cached)
  326.             goto ret1;
  327.     } else
  328.         cno_cnt = (sp->cno - offset_in_line) + 1;
  329.  
  330.     /* This is the loop that actually displays characters. */
  331.     ecbp = (cbp = cbuf) + sizeof(cbuf) - 1;
  332.     for (is_partial = 0, scno = 0;
  333.         offset_in_line < len; ++offset_in_line, offset_in_char = 0) {
  334.         if ((ch = *(u_char *)p++) == '\t' && !list_tab) {
  335.             scno += chlen = TAB_OFF(scno) - offset_in_char;
  336.             is_tab = 1;
  337.         } else {
  338.             scno += chlen = KEY_LEN(sp, ch) - offset_in_char;
  339.             is_tab = 0;
  340.         }
  341.  
  342.         /*
  343.          * Only display up to the right-hand column.  Set a flag if
  344.          * the entire character wasn't displayed for use in setting
  345.          * the cursor.  If reached the end of the line, set the cache
  346.          * info for the screen.  Don't worry about there not being
  347.          * characters to display on the next screen, its lno/off won't
  348.          * match up in that case.
  349.          */
  350.         if (scno >= cols_per_screen) {
  351.             if (is_tab == 1) {
  352.                 chlen -= scno - cols_per_screen;
  353.                 smp->c_ecsize = smp->c_eclen = chlen;
  354.                 scno = cols_per_screen;
  355.             } else {
  356.                 smp->c_ecsize = chlen;
  357.                 chlen -= scno - cols_per_screen;
  358.                 smp->c_eclen = chlen;
  359.  
  360.                 if (scno > cols_per_screen)
  361.                     is_partial = 1;
  362.             }
  363.             smp->c_eboff = offset_in_line;
  364.  
  365.             /* Terminate the loop. */
  366.             offset_in_line = len;
  367.         }
  368.  
  369.         /*
  370.          * If the caller wants the cursor value, and this was the
  371.          * cursor character, set the value.  There are two ways to
  372.          * put the cursor on a character -- if it's normal display
  373.          * mode, it goes on the last column of the character.  If
  374.          * it's input mode, it goes on the first.  In normal mode,
  375.          * set the cursor only if the entire character was displayed.
  376.          */
  377.         if (cno_cnt &&
  378.             --cno_cnt == 0 && (F_ISSET(sp, SC_TINPUT) || !is_partial)) {
  379.             *yp = smp - HMAP;
  380.             if (F_ISSET(sp, SC_TINPUT))
  381.                 *xp = scno - chlen;
  382.             else
  383.                 *xp = scno - 1;
  384.             if (O_ISSET(sp, O_NUMBER) &&
  385.                 !F_ISSET(sp, SC_TINPUT_INFO) && skip_cols == 0)
  386.                 *xp += O_NUMBER_LENGTH;
  387.  
  388.             /* If the line is on the screen, quit. */
  389.             if (is_cached)
  390.                 goto ret1;
  391.         }
  392.  
  393.         /* If the line is on the screen, don't display anything. */
  394.         if (is_cached)
  395.             continue;
  396.  
  397. #define    FLUSH {                                \
  398.     *cbp = '\0';                            \
  399.     (void)gp->scr_addstr(sp, cbuf, cbp - cbuf);            \
  400.     cbp = cbuf;                            \
  401. }
  402.         /*
  403.          * Display the character.  We do tab expansion here because
  404.          * the screen interface doesn't have any way to set the tab
  405.          * length.  Note, it's theoretically possible for chlen to
  406.          * be larger than cbuf, if the user set a impossibly large
  407.          * tabstop.
  408.          */
  409.         if (is_tab)
  410.             while (chlen--) {
  411.                 if (cbp >= ecbp)
  412.                     FLUSH;
  413.                 *cbp++ = TABCH;
  414.             }
  415.         else {
  416.             if (cbp + chlen >= ecbp)
  417.                 FLUSH;
  418.             for (kp = KEY_NAME(sp, ch) + offset_in_char; chlen--;)
  419.                 *cbp++ = *kp++;
  420.         }
  421.     }
  422.  
  423.     if (scno < cols_per_screen) {
  424.         /* If didn't paint the whole line, update the cache. */
  425.         smp->c_ecsize = smp->c_eclen = KEY_LEN(sp, ch);
  426.         smp->c_eboff = len - 1;
  427.  
  428.         /*
  429.          * If not the info/mode line, and O_LIST set, and at the
  430.          * end of the line, and the line ended on this screen,
  431.          * add a trailing $.
  432.          */
  433.         if (list_dollar) {
  434.             ++scno;
  435.  
  436.             chlen = KEY_LEN(sp, '$');
  437.             if (cbp + chlen >= ecbp)
  438.                 FLUSH;
  439.             for (kp = KEY_NAME(sp, '$'); chlen--;)
  440.                 *cbp++ = *kp++;
  441.         }
  442.  
  443.         /* If still didn't paint the whole line, clear the rest. */
  444.         if (scno < cols_per_screen)
  445.             (void)gp->scr_clrtoeol(sp);
  446.     }
  447.  
  448.     /* Flush any buffered characters. */
  449.     if (cbp > cbuf)
  450.         FLUSH;
  451.  
  452. ret1:    (void)gp->scr_move(sp, oldy, oldx);
  453.     return (0);
  454. }
  455.  
  456. /*
  457.  * vs_number --
  458.  *    Repaint the numbers on all the lines.
  459.  *
  460.  * PUBLIC: int vs_number __P((SCR *));
  461.  */
  462. int
  463. vs_number(sp)
  464.     SCR *sp;
  465. {
  466.     GS *gp;
  467.     SMAP *smp;
  468.     VI_PRIVATE *vip;
  469.     size_t len, oldy, oldx;
  470.     int exist;
  471.     char nbuf[10];
  472.  
  473.     gp = sp->gp;
  474.     vip = VIP(sp);
  475.  
  476.     /* No reason to do anything if we're in input mode on the info line. */
  477.     if (F_ISSET(sp, SC_TINPUT_INFO))
  478.         return (0);
  479.  
  480.     /*
  481.      * Try and avoid getting the last line in the file, by getting the
  482.      * line after the last line in the screen -- if it exists, we know
  483.      * we have to to number all the lines in the screen.  Get the one
  484.      * after the last instead of the last, so that the info line doesn't
  485.      * fool us.  (The problem is that file_lline will lie, and tell us
  486.      * that the info line is the last line in the file.) If that test
  487.      * fails, we have to check each line for existence.
  488.      */
  489.     exist = db_exist(sp, TMAP->lno + 1);
  490.  
  491.     (void)gp->scr_cursor(sp, &oldy, &oldx);
  492.     for (smp = HMAP; smp <= TMAP; ++smp) {
  493.         /* Numbers are only displayed for the first screen line. */
  494.         if (O_ISSET(sp, O_LEFTRIGHT)) {
  495.             if (smp->coff != 0)
  496.                 continue;
  497.         } else
  498.             if (smp->soff != 1)
  499.                 continue;
  500.  
  501.         /*
  502.          * The first line of an empty file gets numbered, otherwise
  503.          * number any existing line.
  504.          */
  505.         if (smp->lno != 1 && !exist && !db_exist(sp, smp->lno))
  506.             break;
  507.  
  508.         (void)gp->scr_move(sp, smp - HMAP, 0);
  509.         len = snprintf(nbuf, sizeof(nbuf), O_NUMBER_FMT, smp->lno);
  510.         (void)gp->scr_addstr(sp, nbuf, len);
  511.     }
  512.     (void)gp->scr_move(sp, oldy, oldx);
  513.     return (0);
  514. }
  515.