home *** CD-ROM | disk | FTP | other *** search
/ Media Share 9 / MEDIASHARE_09.ISO / progmisc / tde221.zip / TAB.C < prev    next >
C/C++ Source or Header  |  1993-04-01  |  27KB  |  952 lines

  1. /*
  2.  * Most of the tab routines were gathered into one file.  There is an
  3.  * assembly routine tdeasm.c that expands tabs.  That routine is in
  4.  * assembly to keep screen updates fairly fast.
  5.  *
  6.  * The basic detab and entab algorithms were supplied by Dave Regan,
  7.  *   regan@jacobs.cs.orst.edu
  8.  *
  9.  * For more info on tabs see:
  10.  *
  11.  *     Brian W. Kernighan and P. J. Plauger, _Software Tools_, Addison-
  12.  *     Wesley Publishing Company, Reading, Mass, 1976, pp 18-27 and 35-39,
  13.  *     ISBN 0-20103669-X.
  14.  *
  15.  * The above reference gives info on fixed and variable tabs.  But when
  16.  *  it comes to non-fixed tabs, I prefer "smart" tabs.  Being lazy, I find
  17.  *  it more convenient to let the editor figure variable tabs for me.
  18.  *
  19.  *
  20.  * New editor name:  TDE, the Thomson-Davis Editor.
  21.  * Author:           Frank Davis
  22.  * Date:             June 5, 1991, version 1.0
  23.  * Date:             July 29, 1991, version 1.1
  24.  * Date:             October 5, 1991, version 1.2
  25.  * Date:             January 20, 1992, version 1.3
  26.  * Date:             February 17, 1992, version 1.4
  27.  * Date:             April 1, 1992, version 1.5
  28.  * Date:             June 5, 1992, version 2.0
  29.  * Date:             October 31, 1992, version 2.1
  30.  * Date:             April 1, 1993, version 2.2
  31.  *
  32.  * This program is released into the public domain, Frank Davis.
  33.  *   You may distribute it freely.
  34.  */
  35.  
  36. #include "tdestr.h"
  37. #include "common.h"
  38. #include "tdefunc.h"
  39. #include "define.h"
  40.  
  41.  
  42. /*
  43.  * Name:    tab_key
  44.  * Purpose: To make the necessary changes after the user types the tab key.
  45.  * Date:    June 5, 1991
  46.  * Passed:  window:  pointer to current window
  47.  * Notes:   If in insert mode, then this function adds the required
  48.  *           number of spaces in the file.
  49.  *          If not in insert mode, then tab simply moves the cursor right
  50.  *           the required distance.
  51.  */
  52. int  tab_key( WINDOW *window )
  53. {
  54. int  spaces;    /* the spaces to move to the next tab stop */
  55. char *source;   /* source for block move to make room for c */
  56. char *dest;     /* destination for block move */
  57. int  pad;
  58. int  len;
  59. register int rcol;
  60. int  old_bcol;
  61. register WINDOW *win;   /* put window pointer in a register */
  62. int  rc;
  63.  
  64.    win  = window;
  65.    if (win->ll->len  ==  EOF)
  66.       return( OK );
  67.    rcol = win->rcol;
  68.    old_bcol = win->bcol;
  69.    show_ruler_char( win );
  70.    /*
  71.     * work out the number of spaces to the next tab stop
  72.     */
  73.    if (mode.smart_tab)
  74.       spaces = next_smart_tab( win );
  75.    else
  76.       spaces = mode.ltab_size - (rcol % mode.ltab_size);
  77.  
  78.    assert( spaces >= 0 );
  79.    assert( spaces < MAX_LINE_LENGTH );
  80.  
  81.    rc = OK;
  82.    if (mode.insert && rcol + spaces < g_display.line_length) {
  83.       copy_line( win->ll );
  84.       detab_linebuff( );
  85.  
  86.       /*
  87.        * work out how many characters need to be inserted
  88.        */
  89.       len = g_status.line_buff_len;
  90.       pad = rcol > len ? rcol - len : 0;
  91.       if (len + pad + spaces >= g_display.line_length) {
  92.          /*
  93.           *  line too long to add
  94.           */
  95.          error( WARNING, win->bottom_line, ed1 );
  96.          rc = ERROR;
  97.          g_status.copied = FALSE;
  98.       } else {
  99.          if (pad > 0  || spaces > 0) {
  100.             source = g_status.line_buff + rcol - pad;
  101.             dest = source + pad + spaces;
  102.  
  103.             assert( len + pad - rcol >= 0 );
  104.             assert( len + pad - rcol < MAX_LINE_LENGTH );
  105.  
  106.             memmove( dest, source, len + pad - rcol );
  107.  
  108.             /*
  109.              * if padding was required, then put in the required spaces
  110.              */
  111.  
  112.             assert( pad + spaces >= 0 );
  113.             assert( pad + spaces < MAX_LINE_LENGTH );
  114.  
  115.             memset( source, ' ', pad + spaces );
  116.             g_status.line_buff_len += pad + spaces;
  117.             entab_linebuff( );
  118.          }
  119.  
  120.          win->ll->dirty = TRUE;
  121.          win->file_info->dirty = GLOBAL;
  122.          show_changed_line( win );
  123.          rcol += spaces;
  124.          win->ccol += spaces;
  125.       }
  126.    } else if (rcol + spaces <= g_display.line_length) {
  127.       /*
  128.        * advance the cursor without changing the text underneath
  129.        */
  130.       rcol += spaces;
  131.       win->ccol += spaces;
  132.    }
  133.    check_virtual_col( win, rcol, win->ccol );
  134.    if (old_bcol != win->bcol) {
  135.       make_ruler( win );
  136.       show_ruler( win );
  137.    }
  138.    return( rc );
  139. }
  140.  
  141.  
  142. /*
  143.  * Name:    backtab
  144.  * Purpose: To make the necessary changes after the user presses the backtab.
  145.  * Date:    November 1, 1991
  146.  * Passed:  window:  pointer to current window
  147.  * Notes:   If in insert mode, then this function subs the required
  148.  *           number of spaces in the file.
  149.  *          If not in insert mode, then tab simply moves the cursor left
  150.  *           the required distance.
  151.  */
  152. int  backtab( WINDOW *window )
  153. {
  154. int  spaces;    /* the spaces to move to the next tab stop */
  155. char *source;   /* source for block move to make room for c */
  156. char *dest;     /* destination for block move */
  157. int  pad;
  158. int  len;
  159. register int rcol;
  160. int  old_bcol;
  161. register WINDOW *win;   /* put window pointer in a register */
  162.  
  163.    win  = window;
  164.    rcol = win->rcol;
  165.    if (win->ll->len == EOF || win->rcol == 0)
  166.       return( OK );
  167.    old_bcol = win->bcol;
  168.    show_ruler_char( win );
  169.  
  170.    /*
  171.     * work out the number of spaces to the previous tab stop
  172.     */
  173.    if (mode.smart_tab)
  174.       spaces = prev_smart_tab( win );
  175.    else
  176.       spaces = win->rcol % mode.ltab_size;
  177.  
  178.    if (spaces == 0)
  179.       spaces = mode.ltab_size;
  180.    copy_line( win->ll );
  181.    detab_linebuff( );
  182.    len = g_status.line_buff_len;
  183.    if (mode.insert && rcol - spaces < len) {
  184.       pad = rcol > len ? rcol - len : 0;
  185.       if (pad > 0  || spaces > 0) {
  186.          /*
  187.           * if padding was required, then put in the required spaces
  188.           */
  189.          if (pad > 0) {
  190.  
  191.             assert( rcol - pad >= 0 );
  192.             assert( pad < MAX_LINE_LENGTH );
  193.  
  194.             source = g_status.line_buff + rcol - pad;
  195.             dest = source + pad;
  196.  
  197.             assert( pad >= 0 );
  198.             assert( pad < MAX_LINE_LENGTH );
  199.  
  200.             memmove( dest, source, pad );
  201.             memset( source, ' ', pad );
  202.             g_status.line_buff_len += pad;
  203.          }
  204.          source = g_status.line_buff + rcol;
  205.          dest = source - spaces;
  206.  
  207.          assert( len + pad - rcol >= 0 );
  208.          assert( len + pad - rcol < MAX_LINE_LENGTH );
  209.  
  210.          memmove( dest, source, len + pad - rcol );
  211.          g_status.line_buff_len -= spaces;
  212.          entab_linebuff( );
  213.       }
  214.  
  215.       win->ll->dirty = TRUE;
  216.       win->file_info->dirty = GLOBAL;
  217.       show_changed_line( win );
  218.       rcol -= spaces;
  219.       win->ccol -= spaces;
  220.    } else {
  221.       /*
  222.        * move the cursor without changing the text underneath
  223.        */
  224.       rcol -= spaces;
  225.       if (rcol < 0)
  226.          rcol = 0;
  227.       win->ccol -= spaces;
  228.    }
  229.    check_virtual_col( win, rcol, win->ccol );
  230.    if (old_bcol != win->bcol) {
  231.       make_ruler( win );
  232.       show_ruler( win );
  233.    }
  234.    return( OK );
  235. }
  236.  
  237.  
  238. /*
  239.  * Name:    next_smart_tab
  240.  * Purpose: To find next smart tab
  241.  * Date:    June 5, 1992
  242.  * Passed:  window: pointer to the current window
  243.  * Notes:   To find a smart tab 1) find the first non-blank line above the
  244.  *            current line, 2) find the first non-blank character after
  245.  *            column of the cursor.
  246.  */
  247. int  next_smart_tab( WINDOW *window )
  248. {
  249. register int spaces;    /* the spaces to move to the next tab stop */
  250. text_ptr s;             /* pointer to text */
  251. line_list_ptr ll;
  252. register WINDOW *win;   /* put window pointer in a register */
  253. int  len;
  254.  
  255.    /*
  256.     * find first previous non-blank line above the cursor.
  257.     */
  258.    win = window;
  259.    ll = win->ll->prev;
  260.    while (ll != NULL  && is_line_blank( ll->line, ll->len ))
  261.       ll = ll->prev;
  262.  
  263.    if (ll != NULL) {
  264.       s = ll->line;
  265.       /*
  266.        * if cursor is past the eol of the smart line, lets find the
  267.        *   next fixed tab.
  268.        */
  269.       if (window->rcol >= find_end( s, ll->len ))
  270.          spaces = mode.ltab_size - (window->rcol % mode.ltab_size);
  271.       else {
  272.  
  273.          len = ll->len;
  274.          s = detab_a_line( s, &len );
  275.  
  276.          spaces = 0;
  277.          s = s + window->rcol;
  278.          len -= window->rcol;
  279.  
  280.          /*
  281.           * if we are on a word, find the end of it.
  282.           */
  283.          while (*s != ' '  &&  len > 0) {
  284.             ++s;
  285.             ++spaces;
  286.             --len;
  287.          }
  288.  
  289.          /*
  290.           * now find the start of the next word.
  291.           */
  292.          if (len > 0)
  293.             while (*s == ' ' && len > 0) {
  294.                ++s;
  295.                ++spaces;
  296.                --len;
  297.             }
  298.       }
  299.    } else
  300.       spaces = mode.ltab_size - (window->rcol % mode.ltab_size);
  301.    return( spaces );
  302. }
  303.  
  304.  
  305. /*
  306.  * Name:    prev_smart_tab
  307.  * Purpose: To find previous smart tab
  308.  * Date:    June 5, 1992
  309.  * Passed:  window: pointer to the current window
  310.  * Notes:   To find a smart tab 1) find the first non-blank line above the
  311.  *            current line, 2) find the first non-blank character before
  312.  *            column of the cursor.
  313.  *          there are several cases to consider:  1) the cursor is past the
  314.  *            the end of the smart line, 2) the smart pointer is in the
  315.  *            middle of a word, 3) there are no more words between the
  316.  *            smart pointer and the beginning of the line.
  317.  */
  318. int  prev_smart_tab( WINDOW *window )
  319. {
  320. register int spaces;    /* the spaces to move to the next tab stop */
  321. text_ptr s;             /* pointer to text */
  322. int  len;
  323. line_list_ptr ll;
  324. WINDOW *win;            /* put window pointer in a register */
  325.  
  326.    /*
  327.     * find first previous non-blank line above the cursor, if it exists.
  328.     */
  329.    win = window;
  330.    ll = win->ll->prev;
  331.    while (ll != NULL  && is_line_blank( ll->line, ll->len ))
  332.       ll = ll->prev;
  333.  
  334.    if (ll != NULL) {
  335.       s = ll->line;
  336.  
  337.       /*
  338.        * if there are no words between the cursor and column 1 of the
  339.        *   smart tab line, find previous fixed tab.
  340.        */
  341.       if (window->rcol < first_non_blank( s, ll->len ))
  342.          spaces = window->rcol % mode.ltab_size;
  343.       else {
  344.  
  345.          len = ll->len;
  346.          if (mode.inflate_tabs)
  347.             s = detab_a_line( s, &len );
  348.  
  349.          /*
  350.           * now, we need to figure the initial pointer and space.
  351.           *   if the cursor is past the eol of the smart line, then
  352.           *   set the smart pointer "s" to the end of line and "spaces" to
  353.           *   the number of characters between the cursor and the eol
  354.           *   of the smart line.  otherwise, set the smart pointer "s" to
  355.           *   the column of the cursor and "spaces" to 0.
  356.           */
  357.          if (len < window->rcol) {
  358.             s += len;
  359.             spaces = window->rcol - len;
  360.          } else {
  361.             len = window->rcol;
  362.             s += window->rcol;
  363.             spaces = 0;
  364.          }
  365.  
  366.          while (*(s-1) == ' ' && len > 0) {
  367.             --s;
  368.             ++spaces;
  369.             --len;
  370.          }
  371.  
  372.          /*
  373.           * now find the beginning of the first word at eol.
  374.           */
  375.          while (*(s-1) != ' '  &&  len > 0) {
  376.             --s;
  377.             ++spaces;
  378.             --len;
  379.          }
  380.          if (len == 0 && *s == ' ')
  381.             spaces = window->rcol % mode.ltab_size;
  382.          if (spaces > window->rcol)
  383.             spaces = window->rcol;
  384.       }
  385.    } else
  386.       spaces = window->rcol % mode.ltab_size;
  387.  
  388.    /*
  389.     * spaces cannot be negative.
  390.     */
  391.    if (spaces < 0)
  392.       spaces = 0;
  393.    return( spaces );
  394. }
  395.  
  396.  
  397. /*
  398.  * Name:    entab
  399.  * Purpose: To compress spaces to tabs
  400.  * Date:    October 31, 1992
  401.  * Passed:  s: pointer to current line
  402.  * Returns: pointer to tabout_buf
  403.  * Notes:   the text_ptr can point to either the g_status.line_buff or
  404.  *            a line in the main text buffer.
  405.  *          this function assumes that a '\n' terminates the line.
  406.  */
  407. text_ptr entab( text_ptr s, int len )
  408. {
  409. int  tab_size;
  410. int  last_col;
  411. int  space;
  412. register int col;
  413. text_ptr to;
  414.  
  415.    assert( s != NULL );
  416.    assert( len >= 0 );
  417.    assert( len < MAX_LINE_LENGTH );
  418.  
  419.    tab_size = mode.ptab_size;
  420.    to = (text_ptr)g_status.tabout_buff;
  421.    if (s == NULL)
  422.       g_status.tabout_buff_len = 0;
  423.    else {
  424.       g_status.tabout_buff_len = len;
  425.       for (last_col=col=0; ; s++, len--) {
  426.          if (len == 0) {
  427.  
  428.             /*
  429.              * when we reach the eol, compress trailing spaces to tabs.
  430.              *   the un_copy_line function is responsible for trimming
  431.              *   trailing space.
  432.              */
  433.             if (col != last_col) {
  434.                while (last_col < col) {
  435.                   space = tab_size - last_col % tab_size;
  436.                   if (space <= 1) {
  437.                      *to++ = ' ';
  438.                      last_col++;
  439.                   } else if (last_col + space <= col) {
  440.                      *to++ = '\t';
  441.                      last_col += space;
  442.                      g_status.tabout_buff_len -= (space - 1);
  443.                   } else {
  444.                      *to++ = ' ';
  445.                      last_col++;
  446.                   }
  447.                }
  448.             }
  449.  
  450.             /*
  451.              * stop entabbing when we get to EOL
  452.              */
  453.             break;
  454.          } else if (*s == ' ')
  455.             col++;
  456.          else {
  457.             if (col != last_col) {
  458.                while (last_col < col) {
  459.                   space = tab_size - last_col % tab_size;
  460.  
  461.                   /*
  462.                    * when space == 1, forget about emmitting a tab
  463.                    *   for 1 space.
  464.                    */
  465.                   if (space <= 1) {
  466.                      *to++ = ' ';
  467.                      last_col++;
  468.                   } else if (last_col + space <= col) {
  469.                      *to++ = '\t';
  470.                      last_col += space;
  471.                      g_status.tabout_buff_len -= (space - 1);
  472.                   } else {
  473.                      *to++ = ' ';
  474.                      last_col++;
  475.                   }
  476.                }
  477.             }
  478.  
  479.             /*
  480.              * if *s == tab, then adjust the col pointer
  481.              */
  482.             if (*s == '\t')
  483.                col = col + tab_size - (col % tab_size);
  484.             else
  485.                ++col;
  486.             last_col = col;
  487.             *to++ = *s;
  488.  
  489.             /*
  490.              * when we see a quote character, stop entabbing.
  491.              */
  492.             if (*s == '\"' || *s == '\'') {
  493.                while (len > 0) {
  494.                  *to++ = *++s;
  495.                  --len;
  496.                }
  497.                break;
  498.             }
  499.          }
  500.       }
  501.    }
  502.    return( (text_ptr)g_status.tabout_buff );
  503. }
  504.  
  505.  
  506. /*
  507.  * Name:    detab_linebuff
  508.  * Purpose: To expand tabs in line buffer so we can handle editing functions
  509.  * Date:    October 31, 1992
  510.  * Notes:   before we do any editing function that modifies text, let's
  511.  *            expand any tabs to space.
  512.  *          if inflate_tabs is FALSE, then nothing is done.
  513.  */
  514. void detab_linebuff( void )
  515. {
  516. int  show_eol;
  517. int  len;
  518.  
  519.    if (mode.inflate_tabs  &&  g_status.copied) {
  520.       len = g_status.line_buff_len;
  521.       show_eol = mode.show_eol;
  522.       mode.show_eol = FALSE;
  523.       tabout( (text_ptr)g_status.line_buff, &len );
  524.  
  525.       assert( len >= 0 );
  526.       assert( len < MAX_LINE_LENGTH );
  527.  
  528.       memmove( g_status.line_buff, g_status.tabout_buff, len );
  529.       g_status.line_buff_len = len;
  530.       mode.show_eol = show_eol;
  531.    }
  532. }
  533.  
  534.  
  535. /*
  536.  * Name:    entab_linebuff
  537.  * Purpose: To compress space in line buffer
  538.  * Date:    October 31, 1992
  539.  * Notes:   compress spaces back to tabs in the line buffer, if
  540.  *             inflate_tabs == TRUE
  541.  */
  542. void entab_linebuff( void )
  543. {
  544.    if (mode.inflate_tabs  &&  g_status.copied) {
  545.       entab( (text_ptr)g_status.line_buff, g_status.line_buff_len );
  546.  
  547.       assert( g_status.tabout_buff_len >= 0 );
  548.       assert( g_status.tabout_buff_len < MAX_LINE_LENGTH );
  549.  
  550.       memmove( g_status.line_buff, g_status.tabout_buff,
  551.                        g_status.tabout_buff_len );
  552.       g_status.line_buff_len = g_status.tabout_buff_len;
  553.    }
  554. }
  555.  
  556.  
  557. /*
  558.  * Name:    detab_a_line
  559.  * Purpose: To inflate tabs in any line in the file
  560.  * Date:    October 31, 1992
  561.  * Returns: pointer to text or tabout_buff
  562.  * Notes:   expand an arbitrary line in the file.
  563.  */
  564. text_ptr detab_a_line( text_ptr s, int *len )
  565. {
  566. int  show_eol;
  567.  
  568.    if (mode.inflate_tabs) {
  569.  
  570.       assert( *len >= 0 );
  571.       assert( *len < MAX_LINE_LENGTH );
  572.       assert( s != NULL );
  573.  
  574.       show_eol = mode.show_eol;
  575.       mode.show_eol = FALSE;
  576.       s = tabout( s, len );
  577.       mode.show_eol = show_eol;
  578.    }
  579.    return( s );
  580. }
  581.  
  582.  
  583. /*
  584.  * Name:    detab_adjust_rcol
  585.  * Purpose: given rcol in a line, find virtual column
  586.  * Date:    October 31, 1992
  587.  * Passed:  s:  string pointer
  588.  * Notes:   without expanding tabs, calculate the display column according
  589.  *            to current tab stop.
  590.  */
  591. int  detab_adjust_rcol( text_ptr s, int rcol )
  592. {
  593. register int col;
  594.  
  595.    assert( rcol >= 0 );
  596.    assert( rcol < MAX_LINE_LENGTH );
  597.    assert( s != NULL );
  598.    assert( mode.ptab_size != 0 );
  599.  
  600.    for (col=0; rcol >= 0; rcol--,s++) {
  601.       if (*s == '\t')
  602.          col += (mode.ptab_size - (col % mode.ptab_size));
  603.       else if (rcol > 0)
  604.          col++;
  605.    }
  606.  
  607.    assert( col >= 0 );
  608.    assert( col < MAX_LINE_LENGTH );
  609.  
  610.    return( col );
  611. }
  612.  
  613.  
  614. /*
  615.  * Name:    entab_adjust_rcol
  616.  * Purpose: given virtual rcol in a line, find real column
  617.  * Date:    October 31, 1992
  618.  * Passed:  s:     string pointer
  619.  *          len:   length of string
  620.  *          rcol:  virtual real column
  621.  * Notes:   without expanding tabs, calculate which col the real cursor
  622.  *            referencing.
  623.  */
  624. int  entab_adjust_rcol( text_ptr s, int len, int rcol )
  625. {
  626. register int col;
  627. register int last_col;
  628.  
  629.    assert( len >= 0 );
  630.    assert( len < MAX_LINE_LENGTH );
  631.    assert( rcol >= 0 );
  632.    assert( rcol < MAX_LINE_LENGTH );
  633.    assert( mode.ptab_size != 0 );
  634.  
  635.    if (s != NULL) {
  636.       for (last_col=col=0; col < rcol  &&  s != NULL  &&  len > 0; s++, len--) {
  637.          if (*s != '\t')
  638.             ++col;
  639.          else
  640.             col += (mode.ptab_size - (col % mode.ptab_size));
  641.          if (col > rcol)
  642.             break;
  643.          ++last_col;
  644.       }
  645.    } else
  646.       last_col = rcol;
  647.  
  648.    assert( last_col >= 0 );
  649.    assert( last_col < MAX_LINE_LENGTH );
  650.  
  651.    return( last_col );
  652. }
  653.  
  654.  
  655. /*
  656.  * Name:    block_expand_tabs
  657.  * Purpose: Expand tabs in a marked block.
  658.  * Date:    June 5, 1991
  659.  * Passed:  window:  pointer to current window
  660.  * Notes:   Tabs are expanded using the current tab interval.
  661.  *          Lines are checked to make sure they are not too long.
  662.  */
  663. int  block_expand_tabs( WINDOW *window )
  664. {
  665. int  prompt_line;
  666. int  len;
  667. int  tab;
  668. int  tab_size;
  669. int  dirty;
  670. register int spaces;
  671. line_list_ptr p;                /* pointer to block line */
  672. file_infos *file;
  673. WINDOW *sw, s_w;
  674. long er;
  675. int  i;
  676. int  rc;
  677. char *b;
  678.  
  679.    /*
  680.     * make sure block is marked OK and that this is a LINE block
  681.     */
  682.    prompt_line = window->bottom_line;
  683.  
  684.    if (un_copy_line( window->ll, window, TRUE ) == ERROR)
  685.       return( ERROR );
  686.    check_block( );
  687.    rc = OK;
  688.    if (g_status.marked == TRUE) {
  689.  
  690.       file  = g_status.marked_file;
  691.       if (file->block_type != LINE) {
  692.          /*
  693.           * can only expand tabs in line blocks
  694.           */
  695.          error( WARNING, prompt_line, block20 );
  696.          return( ERROR );
  697.       }
  698.  
  699.       /*
  700.        * initialize everything
  701.        */
  702.       dirty = FALSE;
  703.       tab_size = mode.ptab_size;
  704.       sw = g_status.window_list;
  705.       for (; ptoul( sw->file_info ) != ptoul( file );)
  706.          sw = sw->next;
  707.       dup_window_info( &s_w, sw );
  708.       p  = file->block_start;
  709.       er = file->block_er;
  710.       s_w.rline = file->block_br;
  711.       s_w.visible = FALSE;
  712.       for (; s_w.rline <= er  &&  !g_status.control_break; s_w.rline++) {
  713.  
  714.          /*
  715.           * use the line buffer to expand LINE blocks.
  716.           */
  717.          tab = FALSE;
  718.          g_status.copied = FALSE;
  719.  
  720.          copy_line( p );
  721.          len = g_status.line_buff_len;
  722.          for (b=g_status.line_buff, i=1; len > 0  &&  rc == OK; b++, len--) {
  723.  
  724.             /*
  725.              * each line in the LINE block is copied to the g_status.line_buff.
  726.              *  look at the text in the buffer and expand tabs.
  727.              */
  728.             if (*b == '\t') {
  729.                tab = TRUE;
  730.                spaces = i % tab_size;
  731.                if (spaces)
  732.                   spaces = tab_size - spaces;
  733.                if (spaces) {
  734.  
  735.                   assert( len >= 0 );
  736.                   assert( len < MAX_LINE_LENGTH );
  737.  
  738.                   memmove( b + spaces, b, len );
  739.                }
  740.  
  741.                assert( spaces + 1 >= 0 );
  742.                assert( spaces + 1 < MAX_LINE_LENGTH );
  743.  
  744.                memset( b, ' ', spaces+1 );
  745.                i += spaces + 1;
  746.                b += spaces;
  747.                g_status.line_buff_len += spaces;
  748.             } else
  749.                i++;
  750.          }
  751.  
  752.          /*
  753.           * if any tabs were found, write g_status.line_buff to file.
  754.           */
  755.          if (tab) {
  756.             rc = un_copy_line( p, &s_w, TRUE );
  757.             dirty = TRUE;
  758.          }
  759.          p = p->next;
  760.       }
  761.  
  762.       /*
  763.        * IMPORTANT:  we need to reset the copied flag because the cursor may
  764.        * not necessarily be on the last line of the block.
  765.        */
  766.       g_status.copied = FALSE;
  767.       if (dirty)
  768.          file->dirty = GLOBAL;
  769.    }
  770.    return( rc );
  771. }
  772.  
  773.  
  774. /*
  775.  * Name:    block_compress_tabs
  776.  * Purpose: Expand tabs in a marked block.
  777.  * Date:    October 31, 1992
  778.  * Passed:  window:  pointer to current window
  779.  * Notes:   Tabs are compress using the current tab setting.
  780.  */
  781. int  block_compress_tabs( WINDOW *window )
  782. {
  783. register int col;
  784. register int spaces;
  785. int  len;
  786. int  rc;
  787. int  prompt_line;
  788. int  last_col;
  789. int  tab;
  790. int  tab_size;
  791. int  dirty;
  792. line_list_ptr p;                /* pointer to block line */
  793. text_ptr from;                  /* line in main text buff being compressed */
  794. file_infos *file;
  795. WINDOW *sw, s_w;
  796. long er;
  797. char *to;
  798. int  indent_only;
  799.  
  800.    /*
  801.     * make sure block is marked OK and that this is a LINE block
  802.     */
  803.    prompt_line = window->bottom_line;
  804.    entab_linebuff( );
  805.    if (un_copy_line( window->ll, window, TRUE ) == ERROR)
  806.       return( ERROR );
  807.    check_block( );
  808.    rc = OK;
  809.    if (g_status.marked == TRUE) {
  810.  
  811.       file  = g_status.marked_file;
  812.       if (file->block_type != LINE) {
  813.          /*
  814.           * can only compress tabs in line blocks
  815.           */
  816.          error( WARNING, prompt_line, block26 );
  817.          return( ERROR );
  818.       }
  819.  
  820.       indent_only = g_status.command == BlockIndentTabs ? TRUE : FALSE;
  821.  
  822.       /*
  823.        * set the command to word wrap so the un_copy_line function will
  824.        * not display the lines while expanding.
  825.        */
  826.       g_status.command = WordWrap;
  827.  
  828.       /*
  829.        * initialize everything
  830.        */
  831.       dirty = FALSE;
  832.       tab_size = mode.ptab_size;
  833.       sw = g_status.window_list;
  834.       for (; ptoul( sw->file_info ) != ptoul( file );)
  835.          sw = sw->next;
  836.       dup_window_info( &s_w, sw );
  837.       s_w.visible = FALSE;
  838.       s_w.ll  =  p  = file->block_start;
  839.       er = file->block_er;
  840.       s_w.rline = file->block_br;
  841.       for (; rc == OK  &&  s_w.rline <= er  &&  !g_status.control_break; s_w.rline++) {
  842.  
  843.          /*
  844.           * use the line buffer to compress LINE blocks.
  845.           */
  846.          tab = FALSE;
  847.  
  848.          from = p->line;
  849.          to   = g_status.line_buff;
  850.          g_status.line_buff_len = len  = p->len;
  851.  
  852.          for (last_col=col=0; ; from++, len--) {
  853.             if (len == 0) {
  854.  
  855.                /*
  856.                 * when we reach the eol, compress trailing spaces to tabs.
  857.                 *   the un_copy_line function is responsible for trimming
  858.                 *   trailing space.
  859.                 */
  860.                if (col != last_col) {
  861.                   while (last_col < col) {
  862.                      spaces = tab_size - last_col % tab_size;
  863.                      if (spaces <= 1) {
  864.                         *to++ = ' ';
  865.                         last_col++;
  866.                      } else if (last_col + spaces <= col) {
  867.                         *to++ = '\t';
  868.                         last_col += spaces;
  869.                         g_status.line_buff_len -= (spaces - 1);
  870.                         tab = TRUE;
  871.                      } else {
  872.                         *to++ = ' ';
  873.                         last_col++;
  874.                      }
  875.                   }
  876.                }
  877.  
  878.                /*
  879.                 * stop entabbing when we get to EOL
  880.                 */
  881.                break;
  882.             } else if (*from == ' ')
  883.                col++;
  884.             else {
  885.                if (col != last_col) {
  886.                   while (last_col < col) {
  887.                      spaces = tab_size - last_col % tab_size;
  888.  
  889.                   /*
  890.                    * when space == 1, forget about emmitting a tab
  891.                    *   for 1 space.
  892.                    */
  893.                      if (spaces <= 1) {
  894.                         *to++ = ' ';
  895.                         last_col++;
  896.                      } else if (last_col + spaces <= col) {
  897.                         *to++ = '\t';
  898.                         last_col += spaces;
  899.                         g_status.line_buff_len -= (spaces - 1);
  900.                         tab = TRUE;
  901.                      } else {
  902.                         *to++ = ' ';
  903.                         last_col++;
  904.                      }
  905.                   }
  906.                }
  907.  
  908.                /*
  909.                 * if *from == tab, then adjust the col pointer
  910.                 */
  911.                if (*from == '\t')
  912.                   col = col + tab_size - (col % tab_size);
  913.                else
  914.                   ++col;
  915.                last_col = col;
  916.                *to++ = *from;
  917.  
  918.                /*
  919.                 * when we see a quote character, stop entabbing.
  920.                 */
  921.                if (*from == '\"' || *from == '\''  || indent_only) {
  922.                   while (len > 0) {
  923.                      *to++ = *++from;
  924.                      --len;
  925.                   }
  926.                   break;
  927.                }
  928.             }
  929.          }
  930.  
  931.          /*
  932.           * if any tabs were found, write g_status.line_buff to file.
  933.           */
  934.          if (tab) {
  935.             g_status.copied = TRUE;
  936.             rc = un_copy_line( p, &s_w, TRUE );
  937.             dirty = TRUE;
  938.          }
  939.          p = p->next;
  940.       }
  941.  
  942.       /*
  943.        * IMPORTANT:  we need to reset the copied flag because the cursor may
  944.        * not necessarily be on the last line of the block.
  945.        */
  946.       g_status.copied = FALSE;
  947.       if (dirty)
  948.          file->dirty = GLOBAL;
  949.    }
  950.    return( rc );
  951. }
  952.