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