home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / INFO / EDITOR / TDE120.ZIP / BLOCK.C next >
Encoding:
C/C++ Source or Header  |  1991-10-05  |  50.0 KB  |  1,470 lines

  1. /*******************  start of original comments  ********************/
  2. /*
  3.  * Written by Douglas Thomson (1989/1990)
  4.  *
  5.  * This source code is released into the public domain.
  6.  */
  7.  
  8. /*
  9.  * Name:    dte - Doug's Text Editor program - block commands module
  10.  * Purpose: This file contains all the commands than manipulate blocks.
  11.  * File:    block.c
  12.  * Author:  Douglas Thomson
  13.  * System:  this file is intended to be system-independent
  14.  * Date:    October 1, 1989
  15.  */
  16. /*********************  end of original comments   ********************/
  17.  
  18. /*
  19.  * The block routines have been EXTENSIVELY rewritten.  I am not very fond of
  20.  * stream blocks.  This editor uses LINE blocks and BOX blocks.  That is,
  21.  * one may either mark entire lines or column blocks.  Block operations are
  22.  * done in place.  There are no paste and cut buffers.  In limited memory
  23.  * situations, larger block operations can be carried out.  Block operations
  24.  * can be done within or across files.  One disadvantage of not using buffers
  25.  * is that block operations can be slow.  The most complicated routine in
  26.  * this editor is by far "move_copy_delete_overlay_block( window )".  I put
  27.  * some comments in, but it is still a bitch.
  28.  *
  29.  * Maybe in the next version I'll use buffers to speed up block operations.
  30.  *
  31.  * In tde, version 1.1, I separated the BOX and LINE actions.  LINE actions
  32.  * are a LOT faster, now.  Still need to speed up BOX actions.
  33.  *
  34.  * New editor name:  tde, the Thomson-Davis Editor.
  35.  * Author:           Frank Davis
  36.  * Date:             June 5, 1991
  37.  *
  38.  * This modification of Douglas Thomson's code is released into the
  39.  * public domain, Frank Davis.  You may distribute it freely.
  40.  */
  41.  
  42. #include "tdestr.h"
  43. #include "common.h"
  44. #include "tdefunc.h"
  45. #include "define.h"
  46.  
  47.  
  48. /*
  49.  * Name:    mark_block
  50.  * Purpose: To record the position of the start of the block in the file.
  51.  * Date:    June 5, 1991
  52.  * Passed:  window: information required to access current window
  53.  * Notes:   Assume the user will mark begin and end of a block in either
  54.  *           line mode or box mode.  If the user mixes types, then block
  55.  *           type defaults to LINE.
  56.  */
  57. void mark_block( windows *window )
  58. {
  59. int type;
  60. register file_infos *file;     /* temporary file variable */
  61. int num;
  62. long lnum;
  63.  
  64.    file = window->file_info;
  65.    if (window->rline > file->length)
  66.       return;
  67.    if (g_status.marked == FALSE) {
  68.       g_status.marked = TRUE;
  69.       g_status.marked_file = file;
  70.    }
  71.    if (g_status.command == MarkBlock)
  72.       type = BOX;
  73.    else
  74.       type = LINE;
  75.  
  76.    /*
  77.     * define blocks for only one file.  it is ok to modify blocks in any window
  78.     * pointing to original marked file.
  79.     */
  80.    if (file == g_status.marked_file) {
  81.       g_status.marked_window = window;
  82.  
  83.       /*
  84.        * mark beginning and ending column regardless of block mode.
  85.        */
  86.       if (file->block_type == NOTMARKED) {
  87.          file->block_ec  = file->block_bc = window->rcol;
  88.          file->block_er  = file->block_br = window->rline;
  89.       } else {
  90.          if (file->block_br > window->rline) {
  91.             file->block_br = window->rline;
  92.             if (file->block_bc < window->rcol)
  93.                file->block_ec = window->rcol;
  94.             else
  95.                file->block_bc = window->rcol;
  96.          } else {
  97.             file->block_ec = window->rcol;
  98.             file->block_er = window->rline;
  99.          }
  100.  
  101.          /*
  102.           * if user marks ending line less than beginning line then switch
  103.           */
  104.          if (file->block_er < file->block_br) {
  105.             lnum = file->block_er;
  106.             file->block_er = file->block_br;
  107.             file->block_br = lnum;
  108.          }
  109.  
  110.          /*
  111.           * if user marks ending column less than beginning column then switch
  112.           */
  113.          if (file->block_ec < file->block_bc) {
  114.             num = file->block_ec;
  115.             file->block_ec = file->block_bc;
  116.             file->block_bc = num;
  117.          }
  118.       }
  119.  
  120.       /*
  121.        * block type in now defined.  if user mixes block types then default to
  122.        * LINE block.
  123.        */
  124.       if (file->block_type == NOTMARKED)
  125.          file->block_type = type;
  126.       else if (file->block_type == BOX && type == LINE)
  127.          file->block_type = LINE;
  128.       file->dirty = GLOBAL;
  129.    }
  130. }
  131.  
  132. /*
  133.  * Name:    unmark_block
  134.  * Purpose: To set all block information to NULL or 0
  135.  * Date:    June 5, 1991
  136.  * Passed:  arg_filler: variable to match array of function pointers prototype
  137.  * Notes:   Reset all block variables if marked, otherwise return.
  138.  *           If a block is unmarked then redraw the screen(s).
  139.  */
  140. void unmark_block( windows *arg_filler )
  141. {
  142. register file_infos *marked_file;
  143. windows *window;
  144.  
  145.    if (g_status.marked == TRUE) {
  146.       window = g_status.marked_window;
  147.       marked_file              = g_status.marked_file;
  148.       g_status.marked          = FALSE;
  149.       g_status.marked_file     = NULL;
  150.       g_status.marked_window   = NULL;
  151.       marked_file->block_start = NULL;
  152.       marked_file->block_end   = NULL;
  153.       marked_file->block_bc    = marked_file->block_ec = 0;
  154.       marked_file->block_br    = marked_file->block_er = 0l;
  155.       if (marked_file->block_type)
  156.          marked_file->dirty = GLOBAL;
  157.       marked_file->block_type  = NOTMARKED;
  158.    }
  159. }
  160.  
  161. /*
  162.  * Name:    restore_marked_block
  163.  * Purpose: To restore block beginning and ending row after an editing function
  164.  * Date:    June 5, 1991
  165.  * Passed:  window: information required to access current window
  166.  *          net_change: number of bytes added or subtracted
  167.  * Notes:   If a change has been made before the marked block then the
  168.  *           beginning and ending row need to be adjusted by the number of
  169.  *           lines added or subtracted from file.
  170.  */
  171. void restore_marked_block( windows *window, int net_change )
  172. {
  173. register file_infos *marked_file;
  174. long length;
  175.  
  176.    if (g_status.marked == TRUE && net_change != 0) {
  177.       marked_file = g_status.marked_file;
  178.       length = marked_file->length;
  179.  
  180.       /*
  181.        * restore is needed only if a block is defined and window->file_info is
  182.        * same as marked file and there was a net change in file length.
  183.        */
  184.       if (marked_file == window->file_info) {
  185.  
  186.          /*
  187.           * if cursor is before marked block then adjust block by net change.
  188.           */
  189.          if (marked_file->block_br > window->rline) {
  190.             marked_file->block_br += net_change;
  191.             marked_file->block_er += net_change;
  192.             marked_file->dirty = GLOBAL;
  193.          /*
  194.           * if cursor is somewhere in marked block don't restore, do redisplay
  195.           */
  196.          } else if (marked_file->block_er >= window->rline)
  197.             marked_file->dirty = GLOBAL;
  198.  
  199.          /*
  200.           * check for lines of marked block beyond end of file
  201.           */
  202.          if (marked_file->block_br > length)
  203.             unmark_block( window );
  204.          else if (marked_file->block_er > length) {
  205.             marked_file->block_er = length;
  206.             marked_file->dirty = GLOBAL;
  207.          }
  208.       }
  209.    }
  210. }
  211.  
  212. /*
  213.  * Name:    prepare_block
  214.  * Purpose: To prepare a window/file for a block read, move or copy.
  215.  * Date:    June 5, 1991
  216.  * Passed:  window: current window
  217.  *          file: pointer to file information.
  218.  *          text_line: pointer to line in file to prepare.
  219.  *          lend: line length.
  220.  *          bc: beginning column of BOX.
  221.  * Notes:   The main complication is that the cursor may be beyond the end
  222.  *           of the current line, in which case extra padding spaces have
  223.  *           to be added before the block operation can take place.
  224.  *           This only occurs in BOX operations.
  225.  */
  226. int  prepare_block( windows *window, text_ptr text_line, int lend, int bc )
  227. {
  228. char *source;          /* source for block moves */
  229. char *dest;            /* destination for block moves */
  230. int pad;               /* amount of padding to be added */
  231. register int i;
  232. int number;             /* number of characters for block moves */
  233.  
  234.    copy_line( text_line, window->bottom_line );
  235.  
  236.    /*
  237.     * work out how much padding is required to extend the current
  238.     *  line to the cursor position
  239.     */
  240.  
  241.    pad = bc - lend;
  242.  
  243.    /*
  244.     * make room for the padding spaces
  245.     */
  246.    source = g_status.line_buff + lend;
  247.    dest = source + pad;
  248.    number = pad + 2;
  249.    memmove( dest, source, number );
  250.  
  251.    /*
  252.     * insert the padding spaces
  253.     */
  254.    for (i=pad; i>0; i--)
  255.       *source++ = ' ';
  256.    un_copy_line( text_line, window, FALSE );
  257.    return( pad );
  258. }
  259.  
  260.  
  261. /*
  262.  * Name:    pad_dest_line
  263.  * Purpose: To prepare a window/file for a block move or copy.
  264.  * Date:    June 5, 1991
  265.  * Passed:  window: current window
  266.  *          dest_file: pointer to file information.
  267.  *          dest_line: pointer to line in file to prepare.
  268.  * Notes:   We are doing a BOX action (except DELETE).   We have come to the
  269.  *          end of the file and have no more lines.  All this routine does
  270.  *          is add a blank line to file.
  271.  */
  272. void pad_dest_line( windows *window, file_infos *dest_file, text_ptr dest_line)
  273. {
  274.    /*
  275.     * put linefeed in line_buff. dest_line should be pointing to
  276.     * file->end_text - 1.  since we inserted line feed, increment file length.
  277.     */
  278.    g_status.line_buff[0] = '\n';
  279.    g_status.line_buff[1] = CONTROL_Z;
  280.    g_status.copied = TRUE;
  281.    un_copy_line( dest_line, window, FALSE );
  282.    ++dest_file->length;
  283. }
  284.  
  285.  
  286. /*
  287.  * Name:    move_copy_delete_overlay_block
  288.  * Purpose: Master BOX or LINE routine.
  289.  * Date:    June 5, 1991
  290.  * Passed:  window: information required to access current window
  291.  * Notes:   Operations on BOXs or LINES require several common operations.
  292.  *           All require finding the beginning and ending marks.  The
  293.  *           big differences are whether to delete the source block, copy the
  294.  *           source block, or leave the source block marked.
  295.  *          This routine will handle block operations across files.  Since one
  296.  *           must determine the relationship of source and destination blocks
  297.  *           within a file, it is relatively easy to expand this relationship
  298.  *           across files.  There are several caveats.  Most deal with the
  299.  *           difference between LINE and BOX operations others deal with
  300.  *           differences between operations within a file and operations
  301.  *           across files.
  302.  *          This is probably the most complicated routine in the editor.  It
  303.  *           is not easy to understand.
  304.  */
  305. void move_copy_delete_overlay_block( windows *window )
  306. {
  307. int action;
  308. windows *source_window; /* source window for block moves */
  309. text_ptr source;        /* source for block moves */
  310. text_ptr dest;          /* destination for block moves */
  311. text_ptr p;             /* temporary text pointer */
  312. long number;            /* number of characters for block moves */
  313. int lens;               /* length of source line */
  314. int lend;               /* length of destination line */
  315. int add;                /* characters being added from another line */
  316. int block_len;          /* length of the block */
  317. text_ptr block_start;   /* start of block in file */
  318. text_ptr block_end;     /* end of block in file - not same for LINE or BOX */
  319. char block_buff[BUFF_SIZE+2];
  320. int prompt_line;
  321. int same;               /* are these files the same */
  322. int source_first;       /* is source file lower in memory than dest */
  323. file_infos *source_file, *dest_file;
  324. int rcol, bc, ec;       /* temporary column variables */
  325. int xbc, xec;           /* temporary column variables */
  326. long rline;             /* temporary real line variable */
  327. long br, er, li;        /* temporary line variables */
  328. long dest_add;          /* number of bytes added to destination file */
  329. long source_sub;        /* number of bytes sub from source file */
  330. long diff;
  331. unsigned long block_size;
  332. int block_type;
  333. int fill_char;
  334. windows s_w, d_w;       /* a couple of temporary windows for BOX stuff */
  335.  
  336.    /*
  337.     * initialize block variables
  338.     */
  339.    un_copy_line( window->cursor, window, TRUE );
  340.    if (g_status.marked == FALSE)
  341.       return;
  342.    switch (g_status.command) {
  343.       case MoveBlock :
  344.          action = MOVE;
  345.          break;
  346.       case DeleteBlock :
  347.          action = DELETE;
  348.          break;
  349.       case CopyBlock :
  350.          action = COPY;
  351.          break;
  352.       case KopyBlock :
  353.          action = KOPY;
  354.          break;
  355.       case FillBlock :
  356.          action = FILL;
  357.          break;
  358.       case OverlayBlock :
  359.          action = OVERLAY;
  360.          break;
  361.    }
  362.    source_window = g_status.marked_window;
  363.    prompt_line = window->bottom_line;
  364.    dest_file = window->file_info;
  365.    source_file = g_status.marked_file;
  366.    check_block( );
  367.    if (g_status.marked == FALSE)
  368.       return;
  369.    block_start = source_file->block_start;
  370.    block_end = source_file->block_end;
  371.    block_type = source_file->block_type;
  372.    dest = window->cursor = cpf( window->cursor );
  373.    rline = window->rline;
  374.  
  375.    /*
  376.     * if this is a LINE action, put the text below the current line
  377.     */
  378.    if (block_type == LINE && action != DELETE)
  379.       if ((p = find_next( dest )) != NULL)
  380.          dest = p;
  381.    /*
  382.     * must find out if source and destination file are the same.
  383.     * it don't matter with FILL and DELETE - those actions only modify the
  384.     * source file.
  385.     */
  386.    same = FALSE;
  387.    if (action == FILL) {
  388.       if (block_type == BOX) {
  389.          if (get_block_fill_char( window, &fill_char ) == ERROR)
  390.             return;
  391.          dest = block_start;
  392.          same = TRUE;
  393.       } else {
  394.          error( WARNING, prompt_line, "can only fill box blocks" );
  395.          return;
  396.       }
  397.    }
  398.    if (source_file == dest_file && action != DELETE && action != FILL) {
  399.       same = TRUE;
  400.       if (block_type == BOX && action == MOVE) {
  401.          if (rline == dest_file->block_br  &&
  402.               (window->rcol >= dest_file->block_bc &&
  403.                window->rcol <= dest_file->block_ec))
  404.              /*
  405.               * a block moved to within the block itself has no effect
  406.               */
  407.             return;
  408.       } else if (block_type == LINE) {
  409.          if (rline >= dest_file->block_br && rline <= dest_file->block_er) {
  410.              /*
  411.               * if COPYing or KOPYing within the block itself, reposition the
  412.               * destination to the next line after the block (if it exists)
  413.               */
  414.             if (action == COPY || action == KOPY)
  415.                dest = cpf( block_end );
  416.              /*
  417.               * a block moved to within the block itself has no effect
  418.               */
  419.             else if (action == MOVE)
  420.                return;
  421.          }
  422.       }
  423.    }
  424.  
  425.    /*
  426.     * set up Beginning Column, Ending Column, Beginning Row, Ending Row
  427.     */
  428.    bc = source_file->block_bc;
  429.    ec = source_file->block_ec;
  430.    br = source_file->block_br;
  431.    er = source_file->block_er;
  432.  
  433.    /*
  434.     * if we are BOX FILLing, beginning column is bc, not the column of cursor
  435.     */
  436.    if (action == FILL)
  437.       rcol = bc;
  438.    else
  439.       rcol = window->rcol;
  440.    dest_add = source_sub = 0;
  441.  
  442.    /*
  443.     * must know if source of block is before or after destination
  444.     */
  445.    source_first = FALSE;
  446.    if (ptoul( dest ) > ptoul( source_file->block_start ))
  447.       source_first = TRUE;
  448.    if (same && block_type == BOX) {
  449.       if ( rline >= br)
  450.          source_first = TRUE;
  451.    }
  452.  
  453.    /*
  454.     * work out how much has to be moved
  455.     */
  456.    if (block_type == BOX) {
  457.       block_size = ((ec+1) - bc) * ((er+1) - br);
  458.       if (action != DELETE)
  459.          block_size += ((rcol+1) * ((er+1) - br));
  460.       else
  461.          block_size = 0;
  462.    } else if (block_type == LINE) {
  463.       if (action == COPY || action == KOPY)
  464.          block_size = ptoul( block_end ) - ptoul( block_start );
  465.       else
  466.          block_size = 0;
  467.    } else
  468.       return;
  469.  
  470.    /*
  471.     * check that there is room to add block to file
  472.     */
  473.    if (ptoul( g_status.end_mem ) + block_size >= ptoul( g_status.max_mem )) {
  474.       error( WARNING, prompt_line, "not enough memory for block" );
  475.       return;
  476.    }
  477.  
  478.    /*
  479.     * 1. can't create lines greater than g_display.line_length
  480.     * 2. if we are FILLing a BOX - fill block buff once right here
  481.     * 3. only allow overlaying BOXs
  482.     */
  483.    if (block_type == BOX) {
  484.       block_len = (ec+1) - bc;
  485.       if (action != DELETE && action != FILL) {
  486.          if (rcol + block_len > MAX_LINE_LENGTH) {
  487.             error( WARNING, prompt_line, "line would be too long" );
  488.             return;
  489.          }
  490.       } else if (action == FILL)
  491.          block_fill( block_buff, fill_char, block_len );
  492.    } else {
  493.       block_len = 0;
  494.       if (action == OVERLAY) {
  495.          error( WARNING, prompt_line, "can only overlay blocks" );
  496.          return;
  497.       }
  498.    }
  499.  
  500.    /*
  501.     * all block actions go forward thru file - check those pointers
  502.     */
  503.    source = cpf( block_start );
  504.    dest = cpf( dest );
  505.    if (block_type == LINE) {
  506.       diff = ptoul( block_end ) - ptoul( block_start );
  507.       dest_add = source_sub = diff;
  508.       if (action != DELETE) {
  509.          p = addltop( diff, dest );
  510.          number = ptoul( g_status.end_mem ) - ptoul( dest );
  511.          hw_move( p, dest, number );
  512.          g_status.end_mem = addltop( diff, g_status.end_mem);
  513.       }
  514.       if (action != DELETE && !source_first)
  515.          source = addltop( diff, source );
  516.       if (action == COPY || action == KOPY || action == MOVE)
  517.          hw_move( dest, source, diff );
  518.       if (action == DELETE || action == MOVE) {
  519.          p = addltop( diff, source );
  520.          number = ptoul( g_status.end_mem ) - ptoul( p );
  521.          hw_move( source, p, number );
  522.          g_status.end_mem = addltop( -diff, g_status.end_mem);
  523.       }
  524.       if (action == DELETE)
  525.          dest_add = 0;
  526.       else if (action == COPY || action == KOPY)
  527.          source_sub = 0;
  528.       diff = (er+1l) - br;
  529.       if (action == COPY || action == KOPY || action == MOVE)
  530.          dest_file->length += diff;
  531.       if (action == DELETE || action == MOVE)
  532.          source_file->length -= diff;
  533.       if (action == DELETE && source_window->rline >= br) {
  534.          source_window->rline -= ((er + 1) - br);
  535.          if (source_window->rline < br)
  536.             source_window->rline = br;
  537.       }
  538.       /*
  539.        * the block action is now complete.  restore all the start_text and
  540.        * end_text pointers for all open files.
  541.        */
  542.       if (action == MOVE || action == DELETE)
  543.          restore_start_end( dest_file, source_file, dest_add, -source_sub,
  544.                             source_first );
  545.       else
  546.          restore_start_end( dest_file, source_file, dest_add, source_sub,
  547.                             source_first );
  548.       /*
  549.        * restore all cursors in all windows
  550.        */
  551.       restore_cursors( dest_file, source_file );
  552.    } else {
  553.       dup_window_info( &s_w, source_window );
  554.       dup_window_info( &d_w, window );
  555.       s_w.rline = br;
  556.       for (li=br; li<=er; li++, s_w.rline++, d_w.rline++) {
  557.          lens = linelen( source );
  558.          lend = linelen( dest );
  559.  
  560.          if (action == FILL) {
  561.             s_w.cursor = source;
  562.             add = 0;
  563.             if (lens < (rcol+1))
  564.                add = prepare_block( &s_w, source, lens, rcol );
  565.             add += copy_buff_2file( &s_w, block_buff, source, rcol,
  566.                                 block_len, action );
  567.  
  568.          /*
  569.           * if we are doing a BOX action and both the source and
  570.           * destination are 0 then we have nothing to do.  all LINE actions
  571.           * require processing.
  572.           */
  573.          } else if (lens != 0 || lend != 0) {
  574.  
  575.             /*
  576.              * do actions that may require adding to file
  577.              */
  578.             if (action==MOVE || action==COPY || action==KOPY ||
  579.                 action == OVERLAY) {
  580.                d_w.cursor = dest;
  581.                xbc = bc;
  582.                xec = ec;
  583.                if (action != OVERLAY  && same) {
  584.                   if (rcol < bc && rline > br && rline <=er)
  585.                      if (li >= rline) {
  586.                         xbc = bc + block_len;
  587.                         xec = ec + block_len;
  588.                      }
  589.                }
  590.                load_buff( block_buff, source, xbc, xec, block_type );
  591.                add = 0;
  592.                if (lend < (rcol+1))
  593.                   add = prepare_block( &d_w, dest, lend, rcol );
  594.                add += copy_buff_2file( &d_w, block_buff, dest, rcol,
  595.                                 block_len, action );
  596.                if (!source_first)
  597.                   source += add;
  598.             }
  599.  
  600.             /*
  601.              * do actions that may require deleting from file
  602.              */
  603.             if (action == MOVE || action == DELETE) {
  604.                s_w.cursor = source;
  605.                if (lens >= (bc + 1)) {
  606.                   add = block_len;
  607.                   xbc = bc;
  608.                   if (lens <= (ec + 1))
  609.                      add = lens - bc;
  610.                   if (same && action == MOVE) {
  611.                      if (rcol < bc && rline >= br && rline <=er)
  612.                         if (li >= rline)
  613.                            xbc = bc + block_len;
  614.                   }
  615.                   delete_box_block( &s_w, source, xbc, add, prompt_line );
  616.                   if (action == MOVE && source_first) {
  617.                      if (!same || s_w.rline != d_w.rline) {
  618.                         dest = addltop( -add, dest );
  619.                         dest = cpf( dest );
  620.                      }
  621.                   }
  622.                }
  623.             }
  624.          }
  625.  
  626.          /*
  627.           * if we are doing any BOX action we need to move the source pointer
  628.           * to the next line.
  629.           */
  630.          source = find_next( source );
  631.  
  632.          /*
  633.           * if we are doing any action other than DELETE, we need to move
  634.           * the destination to the next line in marked block.
  635.           * In BOX mode, we may need to pad the end of the file
  636.           * with a blank line before we process the next line.
  637.           */
  638.          if (action != DELETE && action != FILL) {
  639.             p = find_next( dest );
  640.             if (p != NULL && *p != CONTROL_Z)
  641.                dest = p;
  642.             else {
  643.                p = addltop( -1, dest_file->end_text );
  644.                pad_dest_line( window, dest_file, p );
  645.                dest = find_next( dest );
  646.                if (!source_first)
  647.                   ++source;
  648.             }
  649.          }
  650.       }
  651.    }
  652.  
  653.    dest_file->modified = TRUE;
  654.    dest_file->dirty = GLOBAL;
  655.    if (action == MOVE  ||  action == DELETE || action == FILL) {
  656.       source_file->modified = TRUE;
  657.       source_file->dirty = GLOBAL;
  658.    }
  659.  
  660.    /*
  661.     * unless we are doing a KOPY, FILL, or OVERLAY we need to unmark the
  662.     * block.  if we just did a KOPY, the beginning and ending may have
  663.     * changed.  so, we must readjust beginning and ending rows.
  664.     */
  665.    if (action == KOPY) {
  666.       if (same && !source_first && block_type == LINE) {
  667.          number = (er+1) - br;
  668.          source_file->block_br += number;
  669.          source_file->block_er += number;
  670.       } else if (same && !source_first && window->rline == br &&
  671.                  block_type == BOX) {
  672.          add = (ec+1) - bc;
  673.          source_file->block_bc += add;
  674.          source_file->block_ec += add;
  675.       }
  676.    } else if (action != FILL && action != OVERLAY)
  677.       unmark_block( window );
  678.    show_avail_mem( );
  679. }
  680.  
  681.  
  682. /*
  683.  * Name:    load_buff
  684.  * Purpose: copy the contents of a line in a BOX to the block buffer.
  685.  * Date:    June 5, 1991
  686.  * Passed:  block_buff: local buffer for block moves
  687.  *          source:  source line in file
  688.  *          bc:  beginning column of BOX. used only in BOX operations.
  689.  *          ec:  ending column of BOX. used only in BOX operations.
  690.  *          block_type:  LINE or BOX
  691.  * Notes:   If the block is marked in LINE mode, copy the line to the
  692.  *          block buffer.  If the block is marked in BOX mode, there are
  693.  *          several things to take care of.   1) The BOX begins and ends
  694.  *          within a line - just copy the blocked characters to the block buff.
  695.  *          2) the BOX begins within a line but ends past the eol - copy
  696.  *          all the characters within the line to the block buff then fill with
  697.  *          padding.  3) the BOX begins and ends past eol - fill entire
  698.  *          block buff with padding.
  699.  */
  700. void load_buff( char *block_buff, text_ptr source, int bc, int ec,
  701.                 int block_type )
  702. {
  703. int len, pad, avlen;
  704. register int i;
  705.  
  706.    len = linelen( source );
  707.    if (block_type == LINE) {
  708.       if (source[len] == '\n')
  709.          ++len;
  710.       for (i=len; i>0; i++)
  711.          *block_buff++ = *source++;
  712.    } else {
  713.       /*
  714.        * block start may be past eol
  715.        */
  716.       if (len < ec + 1) {
  717.          /*
  718.           * does block start past eol? - fill with pad
  719.           */
  720.          if (len < bc) {
  721.             pad = (ec + 1) - bc;
  722.             for (i=pad; i>0; i--)
  723.                *block_buff++ = ' ';
  724.          } else {
  725.             /*
  726.              * block ends past eol - fill with pad
  727.              */
  728.             pad = (ec + 1) - len;
  729.             avlen = len - bc;
  730.             source = source + bc;
  731.             for (i=avlen; i>0; i--)
  732.                *block_buff++ = *source++;
  733.             for (i=pad; i>0; i--)
  734.                *block_buff++ = ' ';
  735.          }
  736.       } else {
  737.          /*
  738.           * block is within line - copy block to buffer
  739.           */
  740.          avlen = (ec + 1) - bc;
  741.          source = source + bc;
  742.          for (i=avlen; i>0; i--)
  743.             *block_buff++ = *source++;
  744.       }
  745.    }
  746.    *block_buff++ = CONTROL_Z;
  747.    *block_buff = '\0';
  748. }
  749.  
  750.  
  751. /*
  752.  * Name:    copy_buff_2file
  753.  * Purpose: copy the contents of block buffer to destination file
  754.  * Date:    June 5, 1991
  755.  * Passed:  window:  current window
  756.  *          block_buff:  local buffer for moves
  757.  *          dest:  pointer to destination line in destination file
  758.  *          rcol:  if in BOX mode, destination column in destination file
  759.  *          block_len:  if in BOX mode, width of block to copy
  760.  *          action:  type of block action
  761.  * Notes:   In BOX mode, the destination line has already been prepared.
  762.  *          Just copy the BOX buffer to the destination line.
  763.  */
  764. int  copy_buff_2file( windows *window, char *block_buff, text_ptr dest,
  765.                       int rcol, int block_len, int action )
  766. {
  767. char *s;
  768. char *d;
  769. int i;
  770. int rc;
  771.  
  772.    rc = 0;
  773.    copy_line( dest, window->bottom_line );
  774.    s = g_status.line_buff + rcol;
  775.  
  776.    /*
  777.     * s is pointing to location to perform BOX operation.  If we do a
  778.     * FILL or OVERLAY, we do not necessarily add any extra space.  If the
  779.     * line does not extend all the thru the BOX then we add.
  780.     * we always add space when we COPY, KOPY, or MOVE
  781.     */
  782.    if (action == FILL || action == OVERLAY) {
  783.       i = linelen( s );
  784.       if (i < block_len) {
  785.          rc = block_len - i;
  786.          d = s + rc;
  787.          i = block_len + 1 + linelen( g_status.line_buff ) - rcol;
  788.          memmove( d, s, i );
  789.       }
  790.    } else {
  791.       rc = block_len;
  792.       d = s + block_len;
  793.       i = block_len + 1 + linelen( g_status.line_buff ) - rcol;
  794.       memmove( d, s, i );
  795.    }
  796.    memmove( s, block_buff, block_len );
  797.    un_copy_line( dest, window, FALSE );
  798.    return( rc );
  799. }
  800.  
  801.  
  802. /*
  803.  * Name:    block_fill
  804.  * Purpose: fill the block buffer with character
  805.  * Date:    June 5, 1991
  806.  * Passed:  block_buff:  local buffer for moves
  807.  *          fill_char:  fill character
  808.  *          block_len:  number of columns in block
  809.  * Notes:   Fill block_buffer for block_len characters using fill_char.  This
  810.  *          function is used only for BOX blocks.
  811.  */
  812. void block_fill( char *block_buff, int fill_char, int block_len )
  813. {
  814.    memset( block_buff, fill_char, block_len );
  815.    *(block_buff+block_len) = CONTROL_Z;
  816. }
  817.  
  818.  
  819. /*
  820.  * Name:    restore_start_end
  821.  * Purpose: a file has been modified - must restore all start and end pointers
  822.  * Date:    June 5, 1991
  823.  * Passed:  dest_file:  pointer to destination file structure
  824.  *          source_file:  pointer to source file structure
  825.  *          dest_mod:  net modifications in the destination file
  826.  *          source_mod:  net modifications in the source file
  827.  *          source_first:  we must know which file is stored first in memory
  828.  * Notes:   Go through the file list and adjust the start_text and end_text
  829.  *          file pointers as needed.   There are several cases that must be
  830.  *          be considered.  1) destination file and source file could be the
  831.  *          same.  2) if the file pointer we're looking at is below both
  832.  *          the source and destination, no action is needed.  3) the file
  833.  *          we're looking at could be between the source and destination.
  834.  *          4) the file we're looking at could be either source or destination.
  835.  *          5) the file we're looking at could be past both source and dest.
  836.  *          Use unsigned longs to compare pointers.
  837.  */
  838. void restore_start_end( file_infos *dest_file, file_infos *source_file,
  839.                         long dest_mod, long source_mod, int source_first )
  840. {
  841. int same;
  842. long net_mod;
  843. unsigned long sst;      /* source start_text - keep these around for if's */
  844. unsigned long dst;      /* destination start_text */
  845. unsigned long ost;      /* open_file start_text */
  846. register file_infos *open_file;
  847.  
  848.    net_mod = dest_mod + source_mod;
  849.    sst = ptoul( source_file->start_text );
  850.    dst = ptoul( dest_file->start_text );
  851.    same = (sst == dst) ? TRUE : FALSE;
  852.    for (open_file=g_status.file_list; open_file != NULL;
  853.              open_file=open_file->next) {
  854.       sst = ptoul( source_file->start_text );
  855.       dst = ptoul( dest_file->start_text );
  856.       ost = ptoul( open_file->start_text );
  857.       if (ost == sst) {
  858.          if (same)
  859.             source_file->end_text = addltop( net_mod, source_file->end_text);
  860.          else if (source_first)
  861.             source_file->end_text = addltop( source_mod,
  862.                                              source_file->end_text);
  863.          else {
  864.             source_file->start_text = addltop( dest_mod,
  865.                                              source_file->start_text);
  866.             source_file->end_text = addltop( net_mod, source_file->end_text);
  867.          }
  868.       } else if (ost == dst) {
  869.          if (source_first) {
  870.             dest_file->start_text = addltop( source_mod,
  871.                                              dest_file->start_text);
  872.             dest_file->end_text = addltop( net_mod, dest_file->end_text);
  873.          } else
  874.             dest_file->end_text = addltop( dest_mod, dest_file->end_text);
  875.       } else if (ost > sst) {
  876.          if (ost < dst) {
  877.             open_file->start_text = addltop( source_mod,
  878.                                              open_file->start_text);
  879.             open_file->end_text = addltop( source_mod, open_file->end_text);
  880.          } else {
  881.             open_file->start_text = addltop( net_mod, open_file->start_text);
  882.             open_file->end_text = addltop( net_mod, open_file->end_text);
  883.          }
  884.       } else if (ost > dst) {
  885.          if (ost < sst) {
  886.             open_file->start_text = addltop( dest_mod, open_file->start_text);
  887.             open_file->end_text = addltop( dest_mod, open_file->end_text);
  888.          } else {
  889.             open_file->start_text = addltop( net_mod, open_file->start_text);
  890.             open_file->end_text = addltop( net_mod, open_file->end_text);
  891.          }
  892.       }
  893.    }
  894. }
  895.  
  896.  
  897. /*
  898.  * Name:    restore_cursors
  899.  * Purpose: a file has been modified - must restore all cursor pointers
  900.  * Date:    June 5, 1991
  901.  * Passed:  dest_file:  target file for block actions
  902.  *          source_file:  source file for block actions
  903.  * Notes:   Go through the window list and adjust the cursor pointers
  904.  *          as needed.   This could be done by using the changes made by
  905.  *          the block actions, but it would be a real pain in the neck.
  906.  *          I chose to use the brute force approach.
  907.  */
  908. void restore_cursors( file_infos *dest_file, file_infos *source_file )
  909. {
  910. register windows *window;
  911. text_ptr p;
  912. file_infos *file;
  913. long beg_line, cur_line, test_line;
  914. unsigned long df, sf, f;
  915.  
  916.    df = ptoul( (text_ptr)dest_file );
  917.    sf = ptoul( (text_ptr)source_file );
  918.    window = g_status.window_list;
  919.    while (window != NULL) {
  920.       file = window->file_info;
  921.       f = ptoul( (text_ptr)file );
  922.       beg_line = 1;
  923.       cur_line = window->rline;
  924.       if (cur_line > file->length) {
  925.          file->end_text = cpb( file->end_text );
  926.          p = find_prev( file->end_text-1 );
  927.          if (p != NULL )
  928.             window->cursor = p;
  929.          else
  930.             window->cursor = file->start_text;
  931.          window->rline = file->length;
  932.          test_line = cur_line - file->length;
  933.          if (test_line < (long)(window->cline - (window->top_line - 1)))
  934.             window->cline -= test_line;
  935.       } else {
  936.          file->start_text = cpf( file->start_text );
  937.          for (p=file->start_text; p!=NULL && beg_line<cur_line; beg_line++)
  938.             p = find_next( p );
  939.          if (p != NULL )
  940.             window->cursor = p;
  941.          else {
  942.             window->cursor = file->start_text;
  943.             cur_line = file->length;
  944.          }
  945.          window->rline = cur_line;
  946.       }
  947.       if (window->rline <= 0l)
  948.          window->rline = 1l;
  949.       if (window->rline < (window->cline - (window->top_line - 1)))
  950.          window->cline = (int)window->rline + window->top_line - 1;
  951.       if (window->cline < window->top_line)
  952.          window->cline = window->top_line;
  953.       if ((f == df || f == sf) && window->visible )
  954.          show_size( window );
  955.       window = window->next;
  956.    }
  957. }
  958.  
  959.  
  960. /*
  961.  * Name:    delete_box_block
  962.  * Purpose: delete the marked text
  963.  * Date:    June 5, 1991
  964.  * Passed:  s_w:  source window
  965.  *          source:  pointer to line with block to delete
  966.  *          bc:  beginning column of block - BOX mode only
  967.  *          add:  number of characters in block to delete
  968.  *          prompt_line:  line to display error message if needed
  969.  * Notes:   Used only for BOX blocks.  Delete the block.
  970.  */
  971. void delete_box_block( windows *s_w, text_ptr source, int bc, int add,
  972.                        int prompt_line )
  973. {
  974. char *s;
  975. char *d;
  976. register int number;
  977.  
  978.    number = linelen( source ) - bc + 2;
  979.    copy_line( source, prompt_line );
  980.    s = g_status.line_buff + bc + add;
  981.    d = s - add;
  982.    memmove( d, s, number );
  983.    un_copy_line( source, s_w, FALSE );
  984. }
  985.  
  986. /*
  987.  * Name:    check_block
  988.  * Purpose: To check that the block is still valid.
  989.  * Date:    June 5, 1991
  990.  * Notes:   After some editing, the marked block may not be valid.  For example,
  991.  *          deleting all the lines in a block in another window.
  992.  */
  993. void check_block( void )
  994. {
  995. register file_infos *file;
  996. windows filler;
  997.  
  998.    file = g_status.marked_file;
  999.    if (file == NULL || file->block_br > file->length)
  1000.       unmark_block( &filler );
  1001.    else {
  1002.       if (file->length < file->block_er)
  1003.          file->block_er = file->length;
  1004.       find_begblock( file );
  1005.       find_endblock( file );
  1006.    }
  1007. }
  1008.  
  1009.  
  1010. /*
  1011.  * Name:    find_begblock
  1012.  * Purpose: find the beginning line in file with marked block
  1013.  * Date:    June 5, 1991
  1014.  * Passed:  file: file containing marked block
  1015.  * Notes:   file->block_start contains starting line of marked block at end.
  1016.  */
  1017. void find_begblock( file_infos *file )
  1018. {
  1019. text_ptr next;    /* start from beginning of file and go to end */
  1020. long i;           /* line counter */
  1021.  
  1022.    next = cpf( file->start_text );
  1023.    for (i=1; i<file->block_br && next != NULL; i++)
  1024.       next = find_next( next );
  1025.    if (next != NULL)
  1026.       file->block_start = next;
  1027. }
  1028.  
  1029.  
  1030. /*
  1031.  * Name:    find_endblock
  1032.  * Purpose: find the ending line in file with marked block
  1033.  * Date:    June 5, 1991
  1034.  * Passed:  file: file containing marked block
  1035.  * Notes:   If in LINE mode, file->block_end is set to end of line of last
  1036.  *          line in block.  If in BOX mode, file->block_end is set to
  1037.  *          beginning of last line in marked block.  If the search for the
  1038.  *          ending line of the marked block goes past the eof, set the
  1039.  *          ending line of the block to the last line in the file.
  1040.  */
  1041. void find_endblock( file_infos *file )
  1042. {
  1043. text_ptr next;    /* start from beginning of file and go to end */
  1044. long i;           /* line counter */
  1045. int end_column;
  1046. register file_infos *fp;
  1047.  
  1048.    fp = file;
  1049.    next = cpf( fp->start_text );
  1050.    for (i=1; i<fp->block_er && next != NULL; i++)
  1051.       next = find_next( next );
  1052.    if (next != NULL) {
  1053.       end_column = linelen( next );
  1054.       if (next[end_column] == '\n')
  1055.          ++end_column;
  1056.       if (fp->block_type == LINE)
  1057.          fp->block_end = next + end_column;
  1058.       else
  1059.          fp->block_end = next;
  1060.    } else {
  1061.       fp->end_text = cpb( fp->end_text );
  1062.       if (fp->block_type == LINE)
  1063.          fp->block_end = fp->end_text - 1;
  1064.       else {
  1065.          next = find_prev( fp->end_text - 1 );
  1066.          if (next != NULL)
  1067.             fp->block_end = next;
  1068.          else
  1069.             fp->block_end = fp->end_text - 1;
  1070.       }
  1071.       fp->block_er = fp->length;
  1072.    }
  1073. }
  1074.  
  1075. /*
  1076.  * Name:    block_write
  1077.  * Purpose: To write the currently marked block to a disk file.
  1078.  * Date:    June 5, 1991
  1079.  * Passed:  window: information required to access current window
  1080.  * Notes:   If the file already exists, the user gets to choose whether
  1081.  *           to overwrite or append.
  1082.  */
  1083. void block_write( windows *window )
  1084. {
  1085. int prompt_line;
  1086. int rc;
  1087. char buff[MAX_COLS+2]; /* buffer for char and attribute  */
  1088. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  1089. text_ptr block_start;   /* start of block in file */
  1090. text_ptr block_end;     /* end of block in file */
  1091. file_infos *file;
  1092. int block_type;
  1093.  
  1094.    /*
  1095.     * make sure block is marked OK
  1096.     */
  1097.    un_copy_line( window->cursor, window, TRUE );
  1098.    check_block( );
  1099.    if (g_status.marked == TRUE) {
  1100.       prompt_line = window->bottom_line;
  1101.       file        = g_status.marked_file;
  1102.       block_start = file->block_start;
  1103.       block_end   = file->block_end;
  1104.       block_type  = file->block_type;
  1105.  
  1106.       /*
  1107.        * find out which file to write to
  1108.        */
  1109.       save_screen_line( 0, prompt_line, line_buff );
  1110.       if (get_name( "Filename: ", prompt_line, g_status.rw_name,
  1111.                     g_display.message_color ) == OK) {
  1112.          /*
  1113.           * if the file exists, find out whether to overwrite or append
  1114.           */
  1115.          if (hw_fattrib( g_status.rw_name ) != ERROR) {
  1116.             set_prompt( "File exists. Overwrite or Append? (o/a): ",
  1117.                          prompt_line );
  1118.             switch (get_oa( )) {
  1119.                case A_OVERWRITE :
  1120.                   hw_unlink( g_status.rw_name, prompt_line );
  1121.                   combine_strings( buff, "writing block to '",
  1122.                                    g_status.rw_name, "'" );
  1123.                   s_output( buff, prompt_line, 0, g_display.message_color );
  1124.                   rc = hw_save( g_status.rw_name, block_start, block_end,
  1125.                                 block_type );
  1126.                   if (rc == ERROR)
  1127.                      error( WARNING, prompt_line, "could not write block" );
  1128.                   break;
  1129.                case A_APPEND :
  1130.                   combine_strings( buff, "appending block to '",
  1131.                                    g_status.rw_name, "'" );
  1132.                   s_output( buff, prompt_line, 0, g_display.message_color );
  1133.                   rc = hw_append( g_status.rw_name, block_start, block_end,
  1134.                                   block_type );
  1135.                   if (rc == ERROR)
  1136.                      error( WARNING, prompt_line, "could not append block" );
  1137.                   break;
  1138.             }
  1139.          } else {
  1140.             combine_strings( buff, "writing block to '", g_status.rw_name,
  1141.                              "'" );
  1142.             s_output( buff, prompt_line, 0, g_display.message_color );
  1143.             if (hw_save( g_status.rw_name, block_start, block_end,
  1144.                          block_type ) == ERROR)
  1145.                error( WARNING, prompt_line, "could not write block" );
  1146.          }
  1147.       }
  1148.       restore_screen_line( 0, prompt_line, line_buff );
  1149.    }
  1150. }
  1151.  
  1152.  
  1153. /*
  1154.  * Name:    block_print
  1155.  * Purpose: Print an entire file or the currently marked block.
  1156.  * Date:    June 5, 1991
  1157.  * Passed:  window: information required to access current window
  1158.  */
  1159. void block_print( windows *window )
  1160. {
  1161. char answer[MAX_COLS];  /* entire file or just marked block? */
  1162. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  1163. int col, func;
  1164. int prompt_line;
  1165. text_ptr block_start;   /* start of block in file */
  1166. text_ptr block_end;     /* end of block in file */
  1167. file_infos *file;
  1168. int block_type;
  1169. FILE *fp;               /* file pointer for PRN device */
  1170. char *p;
  1171. int len;
  1172. int bc, ec, last_c;
  1173. long lend;
  1174. long l;
  1175. int color;
  1176.  
  1177.    color = g_display.message_color;
  1178.    un_copy_line( window->cursor, window, TRUE );
  1179.    prompt_line = window->bottom_line;
  1180.    save_screen_line( 0, prompt_line, line_buff );
  1181.    /*
  1182.     * print entire file or just marked block?
  1183.     */
  1184.    strcpy( answer, "Print file or block? (f/b): " );
  1185.    col = strlen( answer );
  1186.    s_output( answer, prompt_line, 0, color );
  1187.    eol_clear( col, prompt_line, g_display.text_color );
  1188.    xygoto( col, prompt_line );
  1189.    func = col = 0;
  1190.    while (col != 'f' && col != 'F' && col != 'b' && col != 'B' &&
  1191.           func != AbortCommand) {
  1192.       col = getkey( );
  1193.       func = getfunc( col );
  1194.       if (col == ESC)
  1195.          func = AbortCommand;
  1196.    }
  1197.    if (func != AbortCommand) {
  1198.       file = window->file_info;
  1199.       if (col == 'f' || col == 'F') {
  1200.          block_start = file->start_text;
  1201.          block_end   = cpb( file->end_text ) - 1;
  1202.          block_type = NOTMARKED;
  1203.       } else if (col == 'b' || col == 'B') {
  1204.          check_block( );
  1205.          if (g_status.marked == TRUE) {
  1206.             file        = g_status.marked_file;
  1207.             block_start = file->block_start;
  1208.             block_end   = file->block_end;
  1209.             block_type  = file->block_type;
  1210.          } else
  1211.             col = AbortCommand;
  1212.       }
  1213.       if (block_type != BOX) {
  1214.          block_end = cpb( block_end );
  1215.          block_end = find_prev( block_end );
  1216.       }
  1217.       if (col != AbortCommand) {
  1218.          if ((fp = fopen( "PRN", "a" )) == NULL)
  1219.             error( WARNING, prompt_line, "error in printing text" );
  1220.          else {
  1221.             eol_clear( 0, prompt_line, color );
  1222.             s_output( "Printing line          Press ESC to cancel.",
  1223.                       prompt_line, 0, color );
  1224.             xygoto( 14, prompt_line );
  1225.             block_start = cpf( block_start );
  1226.             if (block_type == BOX) {
  1227.                bc = file->block_bc;
  1228.                ec = file->block_ec;
  1229.                last_c = ec + 1 - bc;
  1230.             }
  1231.             p = g_status.line_buff;
  1232.             lend = ptoul( block_end );
  1233.             for (l=1,col=OK; ptoul( block_start ) <= lend && col == OK; l++) {
  1234.                s_output( "        ", prompt_line, 14, color );
  1235.                ltoa( l, answer, 10 );
  1236.                s_output( answer, prompt_line, 14, color );
  1237.                g_status.copied = FALSE;
  1238.                if (block_type == BOX) {
  1239.                   load_buff( p, block_start, bc, ec, BOX );
  1240.                   *(p+last_c) = '\n';
  1241.                   *(p+last_c+1) = CONTROL_Z;
  1242.                } else
  1243.                   copy_line( block_start, prompt_line );
  1244.                len = find_CONTROL_Z( p );
  1245.                if (fwrite( p, sizeof( char ), len, fp ) < len)
  1246.                   col = ERROR;
  1247.                block_start = find_next( block_start );
  1248.                if (block_start == NULL)
  1249.                   block_start = block_end + 1;
  1250.                if (kbhit()) {
  1251.                   col = getkey( );
  1252.                   if (col == ESC)
  1253.                      col = AbortCommand;
  1254.                   else
  1255.                      col = getfunc( col );
  1256.                   if (col != AbortCommand)
  1257.                      col = OK;
  1258.                }
  1259.             }
  1260.             g_status.copied = FALSE;
  1261.             fclose( fp );
  1262.          }
  1263.       }
  1264.    }
  1265.    restore_screen_line( 0, prompt_line, line_buff );
  1266. }
  1267.  
  1268.  
  1269. /*
  1270.  * Name:    get_block_fill_char
  1271.  * Purpose: get the character to fill marked block.
  1272.  * Date:    June 5, 1991
  1273.  * Passed:  window: information required to access current window
  1274.  *          c: address of character to fill block
  1275.  */
  1276. int  get_block_fill_char( windows *window, int *c )
  1277. {
  1278. char answer[MAX_COLS];  /* entire file or just marked block? */
  1279. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  1280. register int col;
  1281. int prompt_line;
  1282. int rc;
  1283.  
  1284.    rc = OK;
  1285.    prompt_line = window->bottom_line;
  1286.    save_screen_line( 0, prompt_line, line_buff );
  1287.    strcpy( answer, "Enter character to fill block (ESC to exit): " );
  1288.    s_output( answer, prompt_line, 0, g_display.message_color );
  1289.    col = strlen( answer );
  1290.    eol_clear( col, prompt_line, g_display.text_color );
  1291.    xygoto( col, prompt_line );
  1292.    col = getkey( );
  1293.    if (col >= 256)
  1294.       rc = ERROR;
  1295.    else
  1296.       *c = col;
  1297.    restore_screen_line( 0, prompt_line, line_buff );
  1298.    return( rc );
  1299. }
  1300.  
  1301.  
  1302. /*
  1303.  * Name:    block_expand_tabs
  1304.  * Purpose: Expand tabs in a marked block.
  1305.  * Date:    June 5, 1991
  1306.  * Passed:  window:  pointer to current window
  1307.  * Notes:   Tabs are expanded using the current tab interval.
  1308.  *          Lines are checked to make sure they are not too long.
  1309.  */
  1310. void block_expand_tabs( windows *window )
  1311. {
  1312. int prompt_line;
  1313. int len;
  1314. int tab;
  1315. int tab_size;
  1316. int dirty;
  1317. int spaces;
  1318. int net_change;
  1319. text_ptr p;                     /* pointer to block line */
  1320. file_infos *file;
  1321. windows s_w;
  1322. int block_type;
  1323. long er, li;
  1324. int bc, ec;
  1325. int i, j;
  1326. char exp_buff[BUFF_SIZE+2], *b, *q, *s, *d, *lb;
  1327.  
  1328.    /*
  1329.     * make sure block is marked OK
  1330.     */
  1331.    un_copy_line( window->cursor, window, TRUE );
  1332.    check_block( );
  1333.    if (g_status.marked == TRUE) {
  1334.  
  1335.       /*
  1336.        * initialize everything
  1337.        */
  1338.       prompt_line = window->bottom_line;
  1339.       dirty = FALSE;
  1340.       tab_size = mode.tab_size;
  1341.       file  = g_status.marked_file;
  1342.       bc = file->block_bc;
  1343.       ec = file->block_ec;
  1344.       p  = cpf( file->block_start );
  1345.       block_type = file->block_type;
  1346.       er = file->block_er;
  1347.       dup_window_info( &s_w, g_status.marked_window );
  1348.       lb = g_status.line_buff;
  1349.       for (s_w.rline = li = file->block_br; li<=er; li++, s_w.rline++) {
  1350.  
  1351.          /*
  1352.           * use the line buffer to expand LINE blocks.
  1353.           * use the line buffer and the exp buffer to expand BOX blocks.
  1354.           */
  1355.          tab = FALSE;
  1356.          len = linelen( p );
  1357.          net_change = 0;
  1358.          g_status.copied = FALSE;
  1359.          if (block_type == BOX) {
  1360.             if (len > bc) {
  1361.  
  1362.                /*
  1363.                 * copy the line starting at the beginning column of BOX to the
  1364.                 * line buffer.
  1365.                 */
  1366.                copy_line( p+bc, prompt_line);
  1367.                b = lb;
  1368.  
  1369.                /*
  1370.                 * put the characters in the BOX in the exp_buff.
  1371.                 */
  1372.                for (j=0,i=bc; i<= ec && i<len; i++,j++)
  1373.                   exp_buff[j] = *b++;
  1374.                exp_buff[j] = CONTROL_Z;
  1375.  
  1376.                /*
  1377.                 * now, delete all characters in BOX from line buffer.
  1378.                 */
  1379.                b = lb;
  1380.                i = (ec < len) ? ec+1 : len;
  1381.                i -= bc;
  1382.                s = b + i;
  1383.                j = linelen( s ) + 3;
  1384.                memmove( b, s, j );
  1385.  
  1386.                /*
  1387.                 * expand the tabs in the line buffer.
  1388.                 */
  1389.                b = exp_buff;
  1390.                for (b=exp_buff, i=bc+1; *b != CONTROL_Z; b++) {
  1391.                   if (*b == '\t') {
  1392.                      tab = TRUE;
  1393.                      spaces = i % tab_size;
  1394.                      if (spaces)
  1395.                         spaces = tab_size - spaces;
  1396.                      if (spaces) {
  1397.                         d = b + spaces;
  1398.                         j = linelen( b ) + 2;
  1399.                         memmove( d, b, j );
  1400.                      }
  1401.                      for (j=0; j<=spaces; j++)
  1402.                         *(b+j) =  ' ';
  1403.                      net_change += spaces;
  1404.                      i += spaces + 1;
  1405.                      b += spaces;
  1406.                   } else
  1407.                      i++;
  1408.                }
  1409.                if (tab == TRUE) {
  1410.  
  1411.                   /*
  1412.                    * make room in the line buffer for the expanded tabs.
  1413.                    */
  1414.                   i = linelen( exp_buff );
  1415.                   s = lb;
  1416.                   j = linelen( s ) + 2;
  1417.                   d = s + i;
  1418.                   memmove( d, s, j );
  1419.                   for (q=s, b=exp_buff; *b != CONTROL_Z;)
  1420.                      *q++ = *b++;
  1421.                   un_copy_line( p+bc, &s_w, TRUE );
  1422.                }
  1423.             }
  1424.          } else if (block_type == LINE) {
  1425.  
  1426.             /*
  1427.              * LINE blocks are easy.  copy the line into the line buffer (lb)
  1428.              * and expand the tabs if any.
  1429.              */
  1430.             copy_line( p, prompt_line);
  1431.             for (b=lb, i=1; *b != CONTROL_Z; b++) {
  1432.                if (*b == '\t') {
  1433.                   tab = TRUE;
  1434.                   spaces = i % tab_size;
  1435.                   if (spaces)
  1436.                      spaces = tab_size - spaces;
  1437.                   if (spaces) {
  1438.                      d = b + spaces;
  1439.                      j = linelen( b ) + 2;
  1440.                      memmove( d, b, j );
  1441.                   }
  1442.                   for (j=0; j<=spaces; j++)
  1443.                      *(b+j) =  ' ';
  1444.                   net_change += spaces;
  1445.                   i += spaces + 1;
  1446.                   b += spaces;
  1447.                } else
  1448.                   i++;
  1449.             }
  1450.             if (tab == TRUE)
  1451.                un_copy_line( p, &s_w, TRUE );
  1452.          }
  1453.          if (tab == TRUE)
  1454.             dirty = GLOBAL;
  1455.          p = find_next( p );
  1456.       }
  1457.  
  1458.       /*
  1459.        * IMPORTANT:  we need to reset the copied flag because the cursor may
  1460.        * not necessarily be on the last line of the block.
  1461.        */
  1462.       g_status.copied = FALSE;
  1463.       if (dirty) {
  1464.          check_block( );
  1465.          g_status.marked_file->dirty = dirty;
  1466.          show_avail_mem( );
  1467.       }
  1468.    }
  1469. }
  1470.