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