home *** CD-ROM | disk | FTP | other *** search
/ Black Box 4 / BlackBox.cdr / editors / tde150.arj / BLOCK.C next >
C/C++ Source or Header  |  1992-04-01  |  69KB  |  1,992 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.  This editor uses LINE,
  20.  * STREAM, and BOX blocks.  That is, one may mark entire lines, streams of
  21.  * characters, or column blocks.  Block operations are done in place.  There
  22.  * are no paste and cut buffers.  In limited memory situations, larger block
  23.  * operations can be carried out.  Block operations can be done within or
  24.  * across files.  One disadvantage of not using buffers is that block
  25.  * operations can be slow.  The most complicated routine in this editor is by
  26.  * far "move_copy_delete_overlay_block( window )".  I put some comments in,
  27.  * but it is still a bitch.  Come to think of it, most of these block functions
  28.  * are a bitch.
  29.  *
  30.  * Maybe in the next version I'll use buffers to speed up block operations.
  31.  *
  32.  * In tde, version 1.1, I separated the BOX and LINE actions.  LINE actions
  33.  * are a LOT faster, now.  Still need to speed up BOX actions.
  34.  *
  35.  * In tde, version 1.3, I put STREAM blocks back in.  Added block upper case,
  36.  * block lower case, and block strip high bit.
  37.  *
  38.  * In tde, version 1.4, I added a block number function.  Here at our lab,
  39.  * I often need to number samples, lines, etc..., comes in fairly useful.
  40.  *
  41.  * New editor name:  tde, the Thomson-Davis Editor.
  42.  * Author:           Frank Davis
  43.  * Date:             June 5, 1991, version 1.0
  44.  * Date:             July 29, 1991, version 1.1
  45.  * Date:             October 5, 1991, version 1.2
  46.  * Date:             January 20, 1992, version 1.3
  47.  * Date:             February 17, 1992, version 1.4
  48.  * Date:             April 1, 1992, version 1.5
  49.  *
  50.  * This modification of Douglas Thomson's code is released into the
  51.  * public domain, Frank Davis.  You may distribute it freely.
  52.  */
  53.  
  54. #include <bios.h>
  55. #include "tdestr.h"
  56. #include "common.h"
  57. #include "tdefunc.h"
  58. #include "define.h"
  59.  
  60.  
  61. /*
  62.  * Name:    mark_block
  63.  * Purpose: To record the position of the start of the block in the file.
  64.  * Date:    June 5, 1991
  65.  * Passed:  window: information required to access current window
  66.  * Notes:   Assume the user will mark begin and end of a block in either
  67.  *           line, stream, or box mode.  If the user mixes types, then block
  68.  *           type defaults to current block type.
  69.  */
  70. void mark_block( WINDOW *window )
  71. {
  72. int type;
  73. int num;
  74. long lnum;
  75. register file_infos *file;     /* temporary file variable */
  76. register WINDOW *win;  /* put window pointer in a register */
  77.  
  78.    win  = window;
  79.    file = win->file_info;
  80.    if (win->rline > file->length)
  81.       return;
  82.    if (g_status.marked == FALSE) {
  83.       g_status.marked = TRUE;
  84.       g_status.marked_file = file;
  85.    }
  86.    if (g_status.command == MarkBox)
  87.       type = BOX;
  88.    else if (g_status.command == MarkLine)
  89.       type = LINE;
  90.    else if (g_status.command == MarkStream)
  91.       type = STREAM;
  92.  
  93.    /*
  94.     * define blocks for only one file.  it is ok to modify blocks in any window
  95.     * pointing to original marked file.
  96.     */
  97.    if (file == g_status.marked_file) {
  98.  
  99.       /*
  100.        * mark beginning and ending column regardless of block mode.
  101.        */
  102.       if (file->block_type == NOTMARKED) {
  103.          file->block_ec  = file->block_bc = win->rcol;
  104.          file->block_er  = file->block_br = win->rline;
  105.       } else {
  106.          if (file->block_br > win->rline) {
  107.             file->block_br = win->rline;
  108.             if (file->block_bc < win->rcol && type != STREAM)
  109.                file->block_ec = win->rcol;
  110.             else
  111.                file->block_bc = win->rcol;
  112.          } else {
  113.             if (type != STREAM) {
  114.                file->block_ec = win->rcol;
  115.                file->block_er = win->rline;
  116.             } else {
  117.                if (win->rline == file->block_br &&
  118.                    win->rline == file->block_er) {
  119.                   if (win->rcol < file->block_bc)
  120.                      file->block_bc = win->rcol;
  121.                   else
  122.                      file->block_ec = win->rcol;
  123.                } else if (win->rline == file->block_br)
  124.                   file->block_bc = win->rcol;
  125.                else {
  126.                   file->block_ec = win->rcol;
  127.                   file->block_er = win->rline;
  128.                }
  129.             }
  130.          }
  131.  
  132.          /*
  133.           * if user marks ending line less than beginning line then switch
  134.           */
  135.          if (file->block_er < file->block_br) {
  136.             lnum = file->block_er;
  137.             file->block_er = file->block_br;
  138.             file->block_br = lnum;
  139.          }
  140.  
  141.          /*
  142.           * if user marks ending column less than beginning column then switch
  143.           */
  144.          if ((file->block_ec < file->block_bc) && (type != STREAM ||
  145.               (type == STREAM && file->block_br == file->block_er))) {
  146.             num = file->block_ec;
  147.             file->block_ec = file->block_bc;
  148.             file->block_bc = num;
  149.          }
  150.       }
  151.  
  152.       /*
  153.        * block type in now defined.  if user mixes block types then block
  154.        * is defined as current block type.
  155.        */
  156.       if (file->block_type != NOTMARKED) {
  157.          /*
  158.           * if block type goes to BOX, check to make sure ec is greater than
  159.           * or equal to bc.  ec can be less than bc in STREAM blocks.
  160.           */
  161.          if (type == BOX) {
  162.             if (file->block_ec < file->block_bc) {
  163.                num = file->block_ec;
  164.                file->block_ec = file->block_bc;
  165.                file->block_bc = num;
  166.             }
  167.          }
  168.       }
  169.       file->block_type = type;
  170.       file->dirty = GLOBAL;
  171.    } else
  172.       error( WARNING, win->bottom_line,
  173.              "a block is already defined in another file" );
  174. }
  175.  
  176.  
  177. /*
  178.  * Name:    unmark_block
  179.  * Purpose: To set all block information to NULL or 0
  180.  * Date:    June 5, 1991
  181.  * Passed:  arg_filler: variable to match array of function pointers prototype
  182.  * Notes:   Reset all block variables if marked, otherwise return.
  183.  *           If a block is unmarked then redraw the screen(s).
  184.  */
  185. void unmark_block( WINDOW *arg_filler )
  186. {
  187. register file_infos *marked_file;
  188.  
  189.    if (g_status.marked == TRUE) {
  190.       marked_file              = g_status.marked_file;
  191.       g_status.marked          = FALSE;
  192.       g_status.marked_file     = NULL;
  193.       marked_file->block_start = NULL;
  194.       marked_file->block_end   = NULL;
  195.       marked_file->block_bc    = marked_file->block_ec = 0;
  196.       marked_file->block_br    = marked_file->block_er = 0l;
  197.       if (marked_file->block_type)
  198.          marked_file->dirty = GLOBAL;
  199.       marked_file->block_type  = NOTMARKED;
  200.    }
  201. }
  202.  
  203.  
  204. /*
  205.  * Name:    restore_marked_block
  206.  * Purpose: To restore block beginning and ending row after an editing function
  207.  * Date:    June 5, 1991
  208.  * Passed:  window:     information required to access current window
  209.  *          net_change: number of bytes added or subtracted
  210.  * Notes:   If a change has been made before the marked block then the
  211.  *           beginning and ending row need to be adjusted by the number of
  212.  *           lines added or subtracted from file.
  213.  */
  214. void restore_marked_block( WINDOW *window, int net_change )
  215. {
  216. long length;
  217. register file_infos *marked_file;
  218.  
  219.    if (g_status.marked == TRUE && net_change != 0) {
  220.       marked_file = g_status.marked_file;
  221.       length = marked_file->length;
  222.  
  223.       /*
  224.        * restore is needed only if a block is defined and window->file_info is
  225.        * same as marked file and there was a net change in file length.
  226.        */
  227.       if (marked_file == window->file_info) {
  228.  
  229.          /*
  230.           * if cursor is before marked block then adjust block by net change.
  231.           */
  232.          if (marked_file->block_br > window->rline) {
  233.             marked_file->block_br += net_change;
  234.             marked_file->block_er += net_change;
  235.             marked_file->dirty = GLOBAL;
  236.          /*
  237.           * if cursor is somewhere in marked block don't restore, do redisplay
  238.           */
  239.          } else if (marked_file->block_er >= window->rline)
  240.             marked_file->dirty = GLOBAL;
  241.  
  242.          /*
  243.           * check for lines of marked block beyond end of file
  244.           */
  245.          if (marked_file->block_br > length)
  246.             unmark_block( window );
  247.          else if (marked_file->block_er > length) {
  248.             marked_file->block_er = length;
  249.             marked_file->dirty = GLOBAL;
  250.          }
  251.       }
  252.    }
  253. }
  254.  
  255.  
  256. /*
  257.  * Name:    prepare_block
  258.  * Purpose: To prepare a window/file for a block read, move or copy.
  259.  * Date:    June 5, 1991
  260.  * Passed:  window: current window
  261.  *          file: pointer to file information.
  262.  *          text_line: pointer to line in file to prepare.
  263.  *          lend: line length.
  264.  *          bc: beginning column of BOX.
  265.  * Notes:   The main complication is that the cursor may be beyond the end
  266.  *           of the current line, in which case extra padding spaces have
  267.  *           to be added before the block operation can take place.
  268.  *           This only occurs in BOX and STREAM operations.
  269.  */
  270. int  prepare_block( WINDOW *window, text_ptr text_line, int lend, int bc )
  271. {
  272. register int pad;         /* amount of padding to be added */
  273. register char *source;    /* source for block moves */
  274.  
  275.    copy_line( text_line, window->bottom_line );
  276.  
  277.    /*
  278.     * work out how much padding is required to extend the current
  279.     *  line to the cursor position
  280.     */
  281.  
  282.    pad = bc - lend;
  283.  
  284.    /*
  285.     * make room for the padding spaces
  286.     */
  287.    source = g_status.line_buff + lend;
  288.    memmove( source+pad, source, pad+2 );
  289.  
  290.    /*
  291.     * insert the padding spaces
  292.     */
  293.    memset( source, ' ', pad );
  294.    un_copy_line( text_line, window, FALSE );
  295.    return( pad );
  296. }
  297.  
  298.  
  299. /*
  300.  * Name:    pad_dest_line
  301.  * Purpose: To prepare a window/file for a block move or copy.
  302.  * Date:    June 5, 1991
  303.  * Passed:  window: current window
  304.  *          dest_file: pointer to file information.
  305.  *          dest_line: pointer to line in file to prepare.
  306.  * Notes:   We are doing a BOX action (except DELETE).   We have come
  307.  *          to the end of the file and have no more lines.  All this
  308.  *          routine does is add a blank line to file.
  309.  */
  310. void pad_dest_line( WINDOW *window, file_infos *dest_file, text_ptr dest_line)
  311. {
  312.    /*
  313.     * put linefeed in line_buff. dest_line should be pointing to
  314.     * file->end_text - 1.  since we inserted line feed, increment file length.
  315.     */
  316.    g_status.line_buff[0] = '\n';
  317.    g_status.line_buff[1] = CONTROL_Z;
  318.    g_status.copied = TRUE;
  319.    un_copy_line( dest_line, window, FALSE );
  320.    ++dest_file->length;
  321. }
  322.  
  323.  
  324. /*
  325.  * Name:    move_copy_delete_overlay_block
  326.  * Purpose: Master BOX, STREAM, or LINE routine.
  327.  * Date:    June 5, 1991
  328.  * Passed:  window: information required to access current window
  329.  * Notes:   Operations on BOXs, STREAMs, or LINEs require several common
  330.  *           operations.  All require finding the beginning and ending marks.
  331.  *           The big differences are whether to delete the source block, copy
  332.  *           the source block, or leave the source block marked.
  333.  *          This routine will handle block operations across files.  Since one
  334.  *           must determine the relationship of source and destination blocks
  335.  *           within a file, it is relatively easy to expand this relationship
  336.  *           across files.  There are several caveats.  Most deal with the
  337.  *           difference between LINE and BOX operations others deal with
  338.  *           differences between operations within a file and operations
  339.  *           across files.
  340.  *          This is probably the most complicated routine in the editor.  It
  341.  *           is not easy to understand.
  342.  */
  343. void move_copy_delete_overlay_block( WINDOW *window )
  344. {
  345. int action;
  346. WINDOW *source_window; /* source window for block moves */
  347. text_ptr source;        /* source for block moves */
  348. text_ptr dest;          /* destination for block moves */
  349. text_ptr p;             /* temporary text pointer */
  350. long number;            /* number of characters for block moves */
  351. int lens;               /* length of source line */
  352. int lend;               /* length of destination line */
  353. int add;                /* characters being added from another line */
  354. int block_len;          /* length of the block */
  355. text_ptr block_start;   /* start of block in file */
  356. text_ptr block_end;     /* end of block in file - not same for LINE or BOX */
  357. char block_buff[BUFF_SIZE+2];
  358. int prompt_line;
  359. int same;               /* are these files the same */
  360. int source_first;       /* is source file lower in memory than dest */
  361. file_infos *source_file, *dest_file;
  362. int rcol, bc, ec;       /* temporary column variables */
  363. int xbc, xec;           /* temporary column variables */
  364. long rline;             /* temporary real line variable */
  365. long br, er, li;        /* temporary line variables */
  366. long dest_add;          /* number of bytes added to destination file */
  367. long source_sub;        /* number of bytes sub from source file */
  368. long diff;
  369. long block_num;         /* starting number for block number */
  370. long block_inc;         /* increment to use for block number */
  371. int  block_just;        /* left or right justify numbers? */
  372. unsigned long block_size;
  373. int block_type;
  374. int fill_char;
  375. WINDOW s_w, d_w;       /* a couple of temporary WINDOWs for BOX stuff */
  376. char *ltol = "Error: line would be too long.";
  377. int  padded_file;
  378. WINDOW *w;
  379.  
  380.    /*
  381.     * initialize block variables
  382.     */
  383.    un_copy_line( window->cursor, window, TRUE );
  384.    if (g_status.marked == FALSE)
  385.       return;
  386.    switch (g_status.command) {
  387.       case MoveBlock :
  388.          action = MOVE;
  389.          break;
  390.       case DeleteBlock :
  391.          action = DELETE;
  392.          break;
  393.       case CopyBlock :
  394.          action = COPY;
  395.          break;
  396.       case KopyBlock :
  397.          action = KOPY;
  398.          break;
  399.       case FillBlock :
  400.          action = FILL;
  401.          break;
  402.       case OverlayBlock :
  403.          action = OVERLAY;
  404.          break;
  405.       case NumberBlock :
  406.          action = NUMBER;
  407.          break;
  408.    }
  409.    source_file = g_status.marked_file;
  410.    source_window = g_status.window_list;
  411.    for (; ptoul( source_window->file_info ) != ptoul( source_file );)
  412.       source_window = source_window->next;
  413.    prompt_line = window->bottom_line;
  414.    dest_file = window->file_info;
  415.    check_block( );
  416.    if (g_status.marked == FALSE)
  417.       return;
  418.    block_start = source_file->block_start;
  419.    block_end = source_file->block_end;
  420.    block_type = source_file->block_type;
  421.    dest = window->cursor = cpf( window->cursor );
  422.    rline = window->rline;
  423.  
  424.    /*
  425.     * set up Beginning Column, Ending Column, Beginning Row, Ending Row
  426.     */
  427.    bc = source_file->block_bc;
  428.    ec = source_file->block_ec;
  429.    br = source_file->block_br;
  430.    er = source_file->block_er;
  431.  
  432.    /*
  433.     * if we are BOX FILLing or BOX NUMBERing, beginning column is bc,
  434.     *   not the column of cursor
  435.     */
  436.    rcol =  (action == FILL || action == NUMBER) ? bc : window->rcol;
  437.    dest_add = source_sub = 0;
  438.  
  439.    /*
  440.     * if this is a LINE action, put the text below the current line
  441.     */
  442.    if (block_type == LINE && action != DELETE)
  443.       if ((p = find_next( dest )) != NULL)
  444.          dest = p;
  445.    /*
  446.     * must find out if source and destination file are the same.
  447.     * it don't matter with FILL and DELETE - those actions only modify the
  448.     * source file.
  449.     */
  450.    same = FALSE;
  451.    if (action == FILL) {
  452.       if (block_type == BOX) {
  453.          if (get_block_fill_char( window, &fill_char ) == ERROR)
  454.             return;
  455.          dest = block_start;
  456.          same = TRUE;
  457.       } else {
  458.          error( WARNING, prompt_line, "can only fill box blocks" );
  459.          return;
  460.       }
  461.    }
  462.    if (action == NUMBER) {
  463.       if (block_type == BOX) {
  464.          if (get_block_numbers( window, &block_num, &block_inc, &block_just )
  465.               == ERROR)
  466.             return;
  467.          dest = block_start;
  468.          same = TRUE;
  469.       } else {
  470.          error( WARNING, prompt_line, "can only number box blocks" );
  471.          return;
  472.       }
  473.    }
  474.    if (source_file == dest_file && action != DELETE && action != FILL) {
  475.       same = TRUE;
  476.       if (block_type == BOX && action == MOVE) {
  477.          if (rline == br  &&  (rcol >= bc && rcol <= ec))
  478.              /*
  479.               * a block moved to within the block itself has no effect
  480.               */
  481.             return;
  482.       } else if (block_type == LINE || block_type == STREAM) {
  483.          if (rline >= br && rline <= er) {
  484.             if (block_type == LINE) {
  485.                 /*
  486.                  * if COPYing or KOPYing within the block itself, reposition the
  487.                  * destination to the next line after the block (if it exists)
  488.                  */
  489.                if (action == COPY || action == KOPY)
  490.                   dest = cpf( block_end );
  491.                 /*
  492.                  * a block moved to within the block itself has no effect
  493.                  */
  494.                else if (action == MOVE)
  495.                   return;
  496.             } else {
  497.  
  498.                /*
  499.                 * to find out if cursor is in a STREAM block we have to do
  500.                 * a few more tests.  if cursor is on the beginning row or
  501.                 * ending row, then check the beginning and ending column.
  502.                 */
  503.                if ((rline > br && rline < er) || (rline == br && rcol >= bc) ||
  504.                    (rline == er && rcol <= ec)) {
  505.  
  506.                   /*
  507.                    * if the cursor is in middle of STREAM, make destination
  508.                    * the last character following the STREAM block.
  509.                    */
  510.                   if (action == COPY || action == KOPY) {
  511.                      dest = cpf( block_end );
  512.                      rcol = ec + 1;
  513.                      rline = er;
  514.                   } else if (action == MOVE)
  515.                      return;
  516.                }
  517.             }
  518.          }
  519.       }
  520.    }
  521.  
  522.    /*
  523.     * must know if source of block is before or after destination
  524.     */
  525.    source_first = FALSE;
  526.    if (ptoul( dest ) > ptoul( source_file->block_start ))
  527.       source_first = TRUE;
  528.    if (same && block_type == BOX) {
  529.       if ( rline >= br)
  530.          source_first = TRUE;
  531.    }
  532.  
  533.    /*
  534.     * work out how much has to be moved
  535.     */
  536.    if (block_type == BOX) {
  537.       block_size = ((ec+1) - bc) * ((er+1) - br);
  538.       if (action != DELETE)
  539.          block_size += ((rcol+1) * ((er+1) - br));
  540.       else
  541.          block_size = 0;
  542.    } else if (block_type == LINE || block_type == STREAM) {
  543.       if (action == COPY || action == KOPY)
  544.          block_size = ptoul( block_end ) - ptoul( block_start );
  545.       else
  546.          block_size = 0;
  547.    } else
  548.       return;
  549.  
  550.    /*
  551.     * check that there is room to add block to file
  552.     */
  553.    if (ptoul( g_status.end_mem ) + block_size >= ptoul( g_status.max_mem )) {
  554.       error( WARNING, prompt_line, "not enough memory for block" );
  555.       return;
  556.    }
  557.  
  558.    /*
  559.     * set the command to word wrap so the un_copy_line function will
  560.     * not display the lines while doing block stuff.
  561.     */
  562.    g_status.command = WordWrap;
  563.  
  564.    /*
  565.     * 1. can't create lines greater than g_display.line_length
  566.     * 2. if we are FILLing a BOX - fill block buff once right here
  567.     * 3. only allow overlaying BOXs
  568.     */
  569.    if (block_type == BOX) {
  570.       block_len = (ec+1) - bc;
  571.       if (action != DELETE && action != FILL) {
  572.          if (rcol + block_len > MAX_LINE_LENGTH) {
  573.             error( WARNING, prompt_line, ltol );
  574.             return;
  575.          }
  576.       } else if (action == FILL)
  577.          block_fill( block_buff, fill_char, block_len );
  578.    } else if (block_type == LINE) {
  579.       block_len = 0;
  580.       if (action == OVERLAY) {
  581.          error( WARNING, prompt_line, "can only overlay blocks" );
  582.          return;
  583.       }
  584.    } else if (block_type == STREAM) {
  585.       lend = linelen( block_end );
  586.       if (action == DELETE || action == MOVE) {
  587.  
  588.          /*
  589.           * Is what's left on start of STREAM block line plus what's left at
  590.           * end of STREAM block line too long?
  591.           */
  592.          if (lend > ec)
  593.             lend -= ec;
  594.          else
  595.             lend = 0;
  596.          if (bc + lend > MAX_LINE_LENGTH) {
  597.             error( WARNING, prompt_line, ltol );
  598.             return;
  599.          }
  600.       }
  601.  
  602.       if (action != DELETE) {
  603.  
  604.          /*
  605.           * We are doing a MOVE, COPY, or KOPY.  Find out if what's on the
  606.           * current line plus the start of the STREAM line are too long.
  607.           * Then find out if end of the STREAM line plus what's left of
  608.           * the current line are too long.
  609.           */
  610.          lens = linelen( block_start );
  611.  
  612.          /*
  613.           * if we had to move the destination of the STREAM COPY or KOPY
  614.           * to the end of the STREAM block, then dest and window->cursor
  615.           * will not be the same.  In this case, set length to length of
  616.           * first line in STREAM block.  Then we can add the residue of
  617.           * the first line in block plus residue of the last line of block.
  618.           */
  619.          if (ptoul( dest ) == ptoul( window->cursor ))
  620.             add = linelen( dest );
  621.          else
  622.             add = lens;
  623.  
  624.          /*
  625.           * Is current line plus start of STREAM block line too long?
  626.           */
  627.          if (lens > bc)
  628.             lens -= bc;
  629.          else
  630.             lens = 0;
  631.          if (rcol + lens > MAX_LINE_LENGTH) {
  632.             error( WARNING, prompt_line, ltol );
  633.             return;
  634.          }
  635.  
  636.          /*
  637.           * Is residue of current line plus residue of STREAM block line
  638.           * too long?
  639.           */
  640.          if (add > bc)
  641.             add -= bc;
  642.          else
  643.             add = 0;
  644.          if (lend > ec)
  645.             lend -= ec;
  646.          else
  647.             lend = 0;
  648.          if (add + lend > MAX_LINE_LENGTH) {
  649.             error( WARNING, prompt_line, ltol );
  650.             return;
  651.          }
  652.       }
  653.    }
  654.  
  655.    /*
  656.     * all block actions go forward thru file - check those pointers
  657.     */
  658.    source = cpf( block_start );
  659.    dest = cpf( dest );
  660.    if (block_type == LINE || block_type == STREAM) {
  661.       if (block_type == STREAM) {
  662.          dup_window_info( &s_w, source_window );
  663.          dup_window_info( &d_w, window );
  664.          s_w.rline = br;
  665.          d_w.rline = rline;
  666.  
  667.          /*
  668.           * pad the start of the STREAM block if needed.
  669.           */
  670.          lens = linelen( block_start );
  671.          if (lens < bc+1) {
  672.             add = prepare_block( &s_w, block_start, lens, bc );
  673.             if (br != er)
  674.                block_end += add;
  675.             if (source_first)
  676.                dest += add;
  677.          }
  678.          block_start += bc;
  679.          source = cpf( block_start );
  680.  
  681.          /*
  682.           * pad the end of the STREAM block if needed.
  683.           */
  684.          lens = linelen( block_end );
  685.          if (lens < ec+1) {
  686.             add = prepare_block( &s_w, block_end, lens, ec+1 );
  687.             if (source_first)
  688.                dest += add;
  689.          }
  690.          block_end += ec + 1;
  691.  
  692.          /*
  693.           * pad the destination line if necessary
  694.           */
  695.          lend = linelen( dest );
  696.          if (action==MOVE || action==COPY || action==KOPY) {
  697.             if (lend < rcol+1) {
  698.                add = prepare_block( &d_w, dest, lend, rcol );
  699.                if (!source_first) {
  700.                   source += add;
  701.                   block_start += add;
  702.                   block_end   += add;
  703.                }
  704.             }
  705.             dest += rcol;
  706.          }
  707.       }
  708.  
  709.       diff = ptoul( block_end ) - ptoul( block_start );
  710.       dest_add = source_sub = diff;
  711.       if (action != DELETE) {
  712.          p = addltop( diff, dest );
  713.          number = ptoul( g_status.end_mem ) - ptoul( dest );
  714.          hw_move( p, dest, number );
  715.          g_status.end_mem = addltop( diff, g_status.end_mem);
  716.       }
  717.       if (action != DELETE && !source_first)
  718.          source = addltop( diff, source );
  719.       if (action == COPY || action == KOPY || action == MOVE)
  720.          hw_move( dest, source, diff );
  721.       if (action == DELETE || action == MOVE) {
  722.          p = addltop( diff, source );
  723.          number = ptoul( g_status.end_mem ) - ptoul( p );
  724.          hw_move( source, p, number );
  725.          g_status.end_mem = addltop( -diff, g_status.end_mem);
  726.       }
  727.       if (action == DELETE)
  728.          dest_add = 0;
  729.       else if (action == COPY || action == KOPY)
  730.          source_sub = 0;
  731.       diff =  block_type == LINE ? (er+1l) - br : er - br;
  732.       if (action == COPY || action == KOPY || action == MOVE)
  733.          dest_file->length += diff;
  734.       if (action == DELETE || action == MOVE)
  735.          source_file->length -= diff;
  736.       if (action == DELETE && source_window->rline >= br) {
  737.          source_window->rline -= diff;
  738.          if (source_window->rline < br)
  739.             source_window->rline = br;
  740.       }
  741.       /*
  742.        * the block action is now complete.  restore all the start_text and
  743.        * end_text pointers for all open files.
  744.        */
  745.       if (action == MOVE || action == DELETE)
  746.          restore_start_end( dest_file, source_file, dest_add, -source_sub,
  747.                             source_first );
  748.       else
  749.          restore_start_end( dest_file, source_file, dest_add, source_sub,
  750.                             source_first );
  751.       /*
  752.        * restore all cursors in all windows
  753.        */
  754.       restore_cursors( dest_file, source_file );
  755.    } else {
  756.       padded_file = FALSE;
  757.       dup_window_info( &s_w, source_window );
  758.       dup_window_info( &d_w, window );
  759.       s_w.rline = br;
  760.  
  761.       /*
  762.        * special case for block actions.  since block actions always
  763.        * move forward thru the file, overlapping text in an OVERLAY
  764.        * action don't do right.  make the operation start at the end
  765.        * of the block and work backwards.
  766.        */
  767.       if (action == OVERLAY && same &&  rline > br && rline <= er) {
  768.  
  769.          /*
  770.           * see if we need to add padd lines at eof.
  771.           */
  772.          dest_add = rline - br;
  773.          if (dest_add + er > window->file_info->length) {
  774.             dest_add = dest_add - (window->file_info->length - er);
  775.             for (; dest_add > 0; dest_add--) {
  776.                p = addltop( -1, dest_file->end_text );
  777.                pad_dest_line( window, dest_file, p );
  778.             }
  779.             padded_file = TRUE;
  780.             dup_window_info( &s_w, source_window );
  781.             dup_window_info( &d_w, window );
  782.          }
  783.  
  784.          /*
  785.           * move source and dest pointers to the end of the OVERLAY
  786.           */
  787.          for (li=er-br; li > 0; li--) {
  788.             dest = find_next( dest );
  789.             ++d_w.rline;
  790.             source = find_next( source );
  791.             ++s_w.rline;
  792.          }
  793.  
  794.          /*
  795.           * work backwards so the overlapped OVERLAY block don't use
  796.           * overlayed text to fill the block.
  797.           */
  798.          source = cpb( source );
  799.          dest   = cpb( dest );
  800.          for (li=er; li>=br; li--, s_w.rline--, d_w.rline--) {
  801.             lens = linelen( source );
  802.             lend = linelen( dest );
  803.             if (lens != 0 || lend != 0) {
  804.                d_w.cursor = dest;
  805.                load_box_buff( block_buff, cpf( source ), bc, ec );
  806.                if (lend < (rcol+1))
  807.                   prepare_block( &d_w, cpf( dest ), lend, rcol );
  808.                copy_buff_2file( &d_w, block_buff, cpf( dest ), rcol, block_len,
  809.                                 OVERLAY );
  810.             }
  811.             source = find_prev( source );
  812.             dest = find_prev( dest );
  813.          }
  814.       } else {
  815.          for (li=br; li<=er; li++, s_w.rline++, d_w.rline++) {
  816.             lens = linelen( source );
  817.             lend = linelen( dest );
  818.  
  819.             if (action == FILL || action == NUMBER) {
  820.                s_w.cursor = source;
  821.                add = 0;
  822.                if (lens < (rcol+1))
  823.                   add = prepare_block( &s_w, source, lens, rcol );
  824.                if (action == NUMBER) {
  825.                  number_block_buff( block_buff, block_len, block_num, block_just );
  826.                  block_num += block_inc;
  827.                }
  828.                add += copy_buff_2file( &s_w, block_buff, source, rcol,
  829.                                    block_len, action );
  830.  
  831.             /*
  832.              * if we are doing a BOX action and both the source and
  833.              * destination are 0 then we have nothing to do.
  834.              */
  835.             } else if (lens != 0 || lend != 0) {
  836.  
  837.                /*
  838.                 * do actions that may require adding to file
  839.                 */
  840.                if (action==MOVE || action==COPY || action==KOPY ||
  841.                    action == OVERLAY) {
  842.                   d_w.cursor = dest;
  843.                   xbc = bc;
  844.                   xec = ec;
  845.                   if (action != OVERLAY  && same) {
  846.                      if (rcol < bc && rline > br && rline <=er)
  847.                         if (li >= rline) {
  848.                            xbc = bc + block_len;
  849.                            xec = ec + block_len;
  850.                         }
  851.                   }
  852.                   load_box_buff( block_buff, source, xbc, xec );
  853.                   add = 0;
  854.                   if (lend < (rcol+1))
  855.                      add = prepare_block( &d_w, dest, lend, rcol );
  856.                   add += copy_buff_2file( &d_w, block_buff, dest, rcol,
  857.                                    block_len, action );
  858.                   if (!source_first)
  859.                      source += add;
  860.                }
  861.  
  862.                /*
  863.                 * do actions that may require deleting from file
  864.                 */
  865.                if (action == MOVE || action == DELETE) {
  866.                   s_w.cursor = source;
  867.                   if (lens >= (bc + 1)) {
  868.                      if (same && action == MOVE)
  869.                         lens = linelen( source );
  870.                      add = block_len;
  871.                      xbc = bc;
  872.                      if (lens <= (ec + 1))
  873.                         add = lens - bc;
  874.                      if (same && action == MOVE) {
  875.                         if (rcol < bc && rline >= br && rline <=er)
  876.                            if (li >= rline) {
  877.                               xbc = bc + block_len;
  878.                               if (lens <= (ec + block_len + 1))
  879.                                  add = lens - xbc;
  880.                            }
  881.                      }
  882.                      delete_box_block( &s_w, source, xbc, add, prompt_line );
  883.                      if (action == MOVE && source_first) {
  884.                         if (!same || s_w.rline != d_w.rline) {
  885.                            dest = addltop( -add, dest );
  886.                            dest = cpf( dest );
  887.                         }
  888.                      }
  889.                   }
  890.                }
  891.             }
  892.  
  893.             /*
  894.              * if we are doing any BOX action we need to move the source pointer
  895.              * to the next line.
  896.              */
  897.             source = find_next( source );
  898.  
  899.             /*
  900.              * if we are doing any action other than DELETE, we need to move
  901.              * the destination to the next line in marked block.
  902.              * In BOX mode, we may need to pad the end of the file
  903.              * with a blank line before we process the next line.
  904.              */
  905.             if (action != DELETE && action != FILL && action != NUMBER) {
  906.                p = find_next( dest );
  907.                if (p != NULL && *p != CONTROL_Z)
  908.                   dest = p;
  909.                else {
  910.                   padded_file = TRUE;
  911.                   p = addltop( -1, dest_file->end_text );
  912.                   pad_dest_line( window, dest_file, p );
  913.                   dest = find_next( dest );
  914.                   if (!source_first)
  915.                      ++source;
  916.                }
  917.             }
  918.          }
  919.       }
  920.       if (padded_file) {
  921.          w = g_status.window_list;
  922.          while (w != NULL) {
  923.             if (w->file_info == dest_file && w->visible )
  924.                show_size( w );
  925.             w = w->next;
  926.          }
  927.       }
  928.    }
  929.  
  930.    dest_file->modified = TRUE;
  931.    dest_file->dirty = GLOBAL;
  932.    if (action == MOVE || action == DELETE || action == FILL || action==NUMBER) {
  933.       source_file->modified = TRUE;
  934.       source_file->dirty = GLOBAL;
  935.    }
  936.  
  937.    /*
  938.     * unless we are doing a KOPY, FILL, NUMBER, or OVERLAY we need to unmark
  939.     * the block.  if we just did a KOPY, the beginning and ending may have
  940.     * changed.  so, we must readjust beginning and ending rows.
  941.     */
  942.    if (action == KOPY) {
  943.       if (same && !source_first && block_type == LINE) {
  944.          number = (er+1) - br;
  945.          source_file->block_br += number;
  946.          source_file->block_er += number;
  947.       } else if (same && !source_first && window->rline == br &&
  948.                  block_type == BOX) {
  949.          add = (ec+1) - bc;
  950.          source_file->block_bc += add;
  951.          source_file->block_ec += add;
  952.       }
  953.    } else if (action != FILL && action != OVERLAY && action != NUMBER)
  954.       unmark_block( window );
  955.    show_avail_mem( );
  956.    g_status.copied = FALSE;
  957. }
  958.  
  959.  
  960. /*
  961.  * Name:    load_box_buff
  962.  * Purpose: copy the contents of a BOX to a block buffer.
  963.  * Date:    June 5, 1991
  964.  * Passed:  block_buff: local buffer for block moves
  965.  *          source: source line in file
  966.  *          bc:     beginning column of BOX. used only in BOX operations.
  967.  *          ec:     ending column of BOX. used only in BOX operations.
  968.  * Notes:   For BOX blocks, there are several things to take care of:
  969.  *          1) The BOX begins and ends within a line - just copy the blocked
  970.  *          characters to the block buff.  2) the BOX begins within a line but
  971.  *          ends past the eol - copy all the characters within the line to
  972.  *          the block buff then fill with padding.  3) the BOX begins and
  973.  *          ends past eol - fill entire block buff with padding.
  974.  */
  975. void load_box_buff( char *block_buff, text_ptr source, int bc, int ec )
  976. {
  977. int len, pad, avlen;
  978. register int i;
  979. register char *bb;
  980.  
  981.    bb = block_buff;
  982.    len = linelen( source );
  983.    /*
  984.     * block start may be past eol
  985.     */
  986.    if (len < ec + 1) {
  987.       /*
  988.        * does block start past eol? - fill with pad
  989.        */
  990.       if (len < bc) {
  991.          pad = (ec + 1) - bc;
  992.          for (i=pad; i>0; i--)
  993.             *bb++ = ' ';
  994.       } else {
  995.          /*
  996.           * block ends past eol - fill with pad
  997.           */
  998.          pad = (ec + 1) - len;
  999.          avlen = len - bc;
  1000.          source = source + bc;
  1001.          for (i=avlen; i>0; i--)
  1002.             *bb++ = *source++;
  1003.          for (i=pad; i>0; i--)
  1004.             *bb++ = ' ';
  1005.       }
  1006.    } else {
  1007.       /*
  1008.        * block is within line - copy block to buffer
  1009.        */
  1010.       avlen = (ec + 1) - bc;
  1011.       source = source + bc;
  1012.       for (i=avlen; i>0; i--)
  1013.          *bb++ = *source++;
  1014.    }
  1015.    *bb++ = CONTROL_Z;
  1016.    *bb = '\0';
  1017. }
  1018.  
  1019.  
  1020. /*
  1021.  * Name:    copy_buff_2file
  1022.  * Purpose: copy the contents of block buffer to destination file
  1023.  * Date:    June 5, 1991
  1024.  * Passed:  window:     current window
  1025.  *          block_buff: local buffer for moves
  1026.  *          dest:       pointer to destination line in destination file
  1027.  *          rcol:       if in BOX mode, destination column in destination file
  1028.  *          block_len:  if in BOX mode, width of block to copy
  1029.  *          action:     type of block action
  1030.  * Notes:   In BOX mode, the destination line has already been prepared.
  1031.  *          Just copy the BOX buffer to the destination line.
  1032.  */
  1033. int  copy_buff_2file( WINDOW *window, char *block_buff, text_ptr dest,
  1034.                       int rcol, int block_len, int action )
  1035. {
  1036. register char *s;
  1037. char *d;
  1038. int i;
  1039. int rc;
  1040.  
  1041.    rc = 0;
  1042.    copy_line( dest, window->bottom_line );
  1043.    s = g_status.line_buff + rcol;
  1044.  
  1045.    /*
  1046.     * s is pointing to location to perform BOX operation.  If we do a
  1047.     * FILL or OVERLAY, we do not necessarily add any extra space.  If the
  1048.     * line does not extend all the thru the BOX then we add.
  1049.     * we always add space when we COPY, KOPY, or MOVE
  1050.     */
  1051.    if (action == FILL || action == OVERLAY || action == NUMBER) {
  1052.       i = linelen( s );
  1053.       if (i < block_len) {
  1054.          rc = block_len - i;
  1055.          d = s + rc;
  1056.          i = block_len + 1 + linelen( g_status.line_buff ) - rcol;
  1057.          memmove( d, s, i );
  1058.       }
  1059.    } else {
  1060.       rc = block_len;
  1061.       d = s + block_len;
  1062.       i = block_len + 1 + linelen( g_status.line_buff ) - rcol;
  1063.       memmove( d, s, i );
  1064.    }
  1065.    memmove( s, block_buff, block_len );
  1066.    un_copy_line( dest, window, FALSE );
  1067.    return( rc );
  1068. }
  1069.  
  1070.  
  1071. /*
  1072.  * Name:    block_fill
  1073.  * Purpose: fill the block buffer with character
  1074.  * Date:    June 5, 1991
  1075.  * Passed:  block_buff: local buffer for moves
  1076.  *          fill_char:  fill character
  1077.  *          block_len:  number of columns in block
  1078.  * Notes:   Fill block_buffer for block_len characters using fill_char.  This
  1079.  *          function is used only for BOX blocks.
  1080.  */
  1081. void block_fill( char *block_buff, int fill_char, int block_len )
  1082. {
  1083.    memset( block_buff, fill_char, block_len );
  1084.    *(block_buff+block_len) = CONTROL_Z;
  1085. }
  1086.  
  1087.  
  1088. /*
  1089.  * Name:    number_block_buff
  1090.  * Purpose: put a number into the block buffer
  1091.  * Date:    June 5, 1991
  1092.  * Passed:  block_buff: local buffer for moves
  1093.  *          block_len:  number of columns in block
  1094.  *          block_num:  long number to fill block
  1095.  *          just:       LEFT or RIGHT justified?
  1096.  * Notes:   Fill block_buffer for block_len characters with number.
  1097.  *          This function is used only for BOX blocks.
  1098.  */
  1099. void number_block_buff( char *block_buff, int block_len, long block_num,
  1100.                         int just )
  1101. {
  1102. int len;                /* length of number buffer */
  1103. int i;
  1104. char temp[MAX_COLS];    /* buffer for long number to ascii conversion  */
  1105.  
  1106.    block_fill( block_buff, ' ', block_len );
  1107.    len = strlen( ltoa( block_num, temp, 10 ) );
  1108.    if (just == RIGHT) {
  1109.       block_len--;
  1110.       len--;
  1111.       for (;block_len >= 0 && len >= 0; block_len--, len--)
  1112.          block_buff[block_len] = temp[len];
  1113.    } else {
  1114.       for (i=0; block_len > 0 && i < len; block_len--, i++)
  1115.          block_buff[i] = temp[i];
  1116.    }
  1117. }
  1118.  
  1119.  
  1120. /*
  1121.  * Name:    restore_start_end
  1122.  * Purpose: a file has been modified - must restore all start and end pointers
  1123.  * Date:    June 5, 1991
  1124.  * Passed:  dest_file:  pointer to destination file structure
  1125.  *          source_file:  pointer to source file structure
  1126.  *          dest_mod:  net modifications in the destination file
  1127.  *          source_mod:  net modifications in the source file
  1128.  *          source_first:  we must know which file is stored first in memory
  1129.  * Notes:   Go through the file list and adjust the start_text and end_text
  1130.  *          file pointers as needed.   There are several cases that must be
  1131.  *          be considered.  1) destination file and source file could be the
  1132.  *          same.  2) if the file pointer we're looking at is below both
  1133.  *          the source and destination, no action is needed.  3) the file
  1134.  *          we're looking at could be between the source and destination.
  1135.  *          4) the file we're looking at could be either source or destination.
  1136.  *          5) the file we're looking at could be past both source and dest.
  1137.  *          Use unsigned longs to compare pointers.
  1138.  */
  1139. void restore_start_end( file_infos *df, file_infos *source_file,
  1140.                         long dest_mod, long source_mod, int source_first )
  1141. {
  1142. int same;
  1143. long net_mod;
  1144. unsigned long sst;      /* source start_text - keep these around for if's */
  1145. unsigned long dst;      /* destination start_text */
  1146. unsigned long ost;      /* open_file start_text */
  1147. register file_infos *open_file;
  1148. register file_infos *dest_file;
  1149.  
  1150.    dest_file = df;
  1151.    net_mod = dest_mod + source_mod;
  1152.    sst = ptoul( source_file->start_text );
  1153.    dst = ptoul( dest_file->start_text );
  1154.    same =  sst == dst ? TRUE : FALSE;
  1155.    for (open_file=g_status.file_list; open_file != NULL;
  1156.              open_file=open_file->next) {
  1157.       sst = ptoul( source_file->start_text );
  1158.       dst = ptoul( dest_file->start_text );
  1159.       ost = ptoul( open_file->start_text );
  1160.       if (ost == sst) {
  1161.          if (same)
  1162.             source_file->end_text = addltop( net_mod, source_file->end_text);
  1163.          else if (source_first)
  1164.             source_file->end_text = addltop( source_mod,
  1165.                                              source_file->end_text);
  1166.          else {
  1167.             source_file->start_text = addltop( dest_mod,
  1168.                                              source_file->start_text);
  1169.             source_file->end_text = addltop( net_mod, source_file->end_text);
  1170.          }
  1171.       } else if (ost == dst) {
  1172.          if (source_first) {
  1173.             dest_file->start_text = addltop( source_mod,
  1174.                                              dest_file->start_text);
  1175.             dest_file->end_text = addltop( net_mod, dest_file->end_text);
  1176.          } else
  1177.             dest_file->end_text = addltop( dest_mod, dest_file->end_text);
  1178.       } else if (ost > sst) {
  1179.          if (ost < dst) {
  1180.             open_file->start_text = addltop( source_mod,
  1181.                                              open_file->start_text);
  1182.             open_file->end_text = addltop( source_mod, open_file->end_text);
  1183.          } else {
  1184.             open_file->start_text = addltop( net_mod, open_file->start_text);
  1185.             open_file->end_text = addltop( net_mod, open_file->end_text);
  1186.          }
  1187.       } else if (ost > dst) {
  1188.          if (ost < sst) {
  1189.             open_file->start_text = addltop( dest_mod, open_file->start_text);
  1190.             open_file->end_text = addltop( dest_mod, open_file->end_text);
  1191.          } else {
  1192.             open_file->start_text = addltop( net_mod, open_file->start_text);
  1193.             open_file->end_text = addltop( net_mod, open_file->end_text);
  1194.          }
  1195.       }
  1196.    }
  1197. }
  1198.  
  1199.  
  1200. /*
  1201.  * Name:    restore_cursors
  1202.  * Purpose: a file has been modified - must restore all cursor pointers
  1203.  * Date:    June 5, 1991
  1204.  * Passed:  dest_file:  target file for block actions
  1205.  *          source_file:  source file for block actions
  1206.  * Notes:   Go through the window list and adjust the cursor pointers
  1207.  *          as needed.   This could be done by using the changes made by
  1208.  *          the block actions, but it would be a real pain in the neck.
  1209.  *          I chose to use the brute force approach.
  1210.  */
  1211. void restore_cursors( file_infos *dest_file, file_infos *source_file )
  1212. {
  1213. register WINDOW *window;
  1214. register file_infos *file;
  1215. text_ptr p;
  1216. long beg_line, cur_line, test_line;
  1217. unsigned long df, sf, f;
  1218.  
  1219.    df = ptoul( (text_ptr)dest_file );
  1220.    sf = ptoul( (text_ptr)source_file );
  1221.    window = g_status.window_list;
  1222.    while (window != NULL) {
  1223.       file = window->file_info;
  1224.       f = ptoul( (text_ptr)file );
  1225.       beg_line = 1;
  1226.       cur_line = window->rline;
  1227.       if (cur_line > file->length) {
  1228.          file->end_text = cpb( file->end_text );
  1229.          p = find_prev( file->end_text-1 );
  1230.          window->cursor =  p != NULL ? p : file->start_text;
  1231.          window->rline = file->length;
  1232.          test_line = cur_line - file->length;
  1233.          if (test_line<(long)(window->cline-(window->top_line+window->ruler-1)))
  1234.             window->cline -= test_line;
  1235.       } else {
  1236.          file->start_text = cpf( file->start_text );
  1237.          for (p=file->start_text; p!=NULL && beg_line<cur_line; beg_line++)
  1238.             p = find_next( p );
  1239.          if (p != NULL )
  1240.             window->cursor = p;
  1241.          else {
  1242.             window->cursor = file->start_text;
  1243.             cur_line = file->length;
  1244.          }
  1245.          window->rline = cur_line;
  1246.       }
  1247.       if (window->rline <= 0l)
  1248.          window->rline = 1l;
  1249.       if (window->rline < (window->cline - (window->top_line+window->ruler-1)))
  1250.          window->cline = (int)window->rline + window->top_line+window->ruler-1;
  1251.       if (window->cline < window->top_line + window->ruler)
  1252.          window->cline = window->top_line + window->ruler;
  1253.       if ((f == df || f == sf) && window->visible )
  1254.          show_size( window );
  1255.       window = window->next;
  1256.    }
  1257. }
  1258.  
  1259.  
  1260. /*
  1261.  * Name:    delete_box_block
  1262.  * Purpose: delete the marked text
  1263.  * Date:    June 5, 1991
  1264.  * Passed:  s_w:    source window
  1265.  *          source: pointer to line with block to delete
  1266.  *          bc:     beginning column of block - BOX mode only
  1267.  *          add:    number of characters in block to delete
  1268.  *          prompt_line:  line to display error message if needed
  1269.  * Notes:   Used only for BOX blocks.  Delete the block.
  1270.  */
  1271. void delete_box_block( WINDOW *s_w, text_ptr source, int bc, int add,
  1272.                        int prompt_line )
  1273. {
  1274. char *s;
  1275. int number;
  1276.  
  1277.    number = linelen( source ) - bc + 2;
  1278.    copy_line( source, prompt_line );
  1279.    s = g_status.line_buff + bc + add;
  1280.    memmove( s - add, s, number );
  1281.    un_copy_line( source, s_w, FALSE );
  1282. }
  1283.  
  1284.  
  1285. /*
  1286.  * Name:    check_block
  1287.  * Purpose: To check that the block is still valid.
  1288.  * Date:    June 5, 1991
  1289.  * Notes:   After some editing, the marked block may not be valid.  For example,
  1290.  *          deleting all the lines in a block in another window.  We don't
  1291.  *          need to keep up with the block text pointers while doing normal
  1292.  *          editing; however, we need to refresh them before doing block stuff.
  1293.  */
  1294. void check_block( void )
  1295. {
  1296. register file_infos *file;
  1297. WINDOW filler;
  1298.  
  1299.    file = g_status.marked_file;
  1300.    if (file == NULL || file->block_br > file->length)
  1301.       unmark_block( &filler );
  1302.    else {
  1303.       if (file->length < file->block_er)
  1304.          file->block_er = file->length;
  1305.       find_begblock( file );
  1306.       find_endblock( file );
  1307.    }
  1308. }
  1309.  
  1310.  
  1311. /*
  1312.  * Name:    find_begblock
  1313.  * Purpose: find the beginning line in file with marked block
  1314.  * Date:    June 5, 1991
  1315.  * Passed:  file: file containing marked block
  1316.  * Notes:   file->block_start contains starting line of marked block.
  1317.  */
  1318. void find_begblock( file_infos *file )
  1319. {
  1320. text_ptr next;    /* start from beginning of file and go to end */
  1321. long i;           /* line counter */
  1322.  
  1323.    next = cpf( file->start_text );
  1324.    for (i=1; i<file->block_br && next != NULL; i++)
  1325.       next = find_next( next );
  1326.    if (next != NULL)
  1327.       file->block_start = next;
  1328. }
  1329.  
  1330.  
  1331. /*
  1332.  * Name:    find_endblock
  1333.  * Purpose: find the ending line in file with marked block
  1334.  * Date:    June 5, 1991
  1335.  * Passed:  file: file containing marked block
  1336.  * Notes:   If in LINE mode, file->block_end is set to end of line of last
  1337.  *          line in block.  If in BOX mode, file->block_end is set to
  1338.  *          beginning of last line in marked block.  If the search for the
  1339.  *          ending line of the marked block goes past the eof, set the
  1340.  *          ending line of the block to the last line in the file.
  1341.  */
  1342. void find_endblock( file_infos *file )
  1343. {
  1344. text_ptr next;    /* start from beginning of file and go to end */
  1345. long i;           /* line counter */
  1346. int end_column;
  1347. register file_infos *fp;
  1348.  
  1349.    fp = file;
  1350.    next = cpf( fp->start_text );
  1351.    for (i=1; i<fp->block_er && next != NULL; i++)
  1352.       next = find_next( next );
  1353.    if (next != NULL) {
  1354.       end_column = linelen( next );
  1355.       if (next[end_column] == '\n')
  1356.          ++end_column;
  1357.  
  1358.       /*
  1359.        * if LINE block somewhere in the file, set block_end to first
  1360.        * line past end of marked block.
  1361.        */
  1362.       fp->block_end =  fp->block_type == LINE ? next + end_column : next;
  1363.    } else {
  1364.  
  1365.       /*
  1366.        * last line in marked block is NULL.  if LINE block, set end to
  1367.        * last character in the file.  if STREAM or BOX block, set end to
  1368.        * start of last line in file.  ending row, or er, is then set to
  1369.        * file length.
  1370.        */
  1371.       fp->end_text = cpb( fp->end_text );
  1372.       if (fp->block_type == LINE)
  1373.          fp->block_end = fp->end_text - 1;
  1374.       else {
  1375.          next = find_prev( fp->end_text - 1 );
  1376.          fp->block_end =  next != NULL ? next : fp->end_text - 1;
  1377.       }
  1378.       fp->block_er = fp->length;
  1379.    }
  1380. }
  1381.  
  1382.  
  1383. /*
  1384.  * Name:    block_write
  1385.  * Purpose: To write the currently marked block to a disk file.
  1386.  * Date:    June 5, 1991
  1387.  * Passed:  window: information required to access current window
  1388.  * Notes:   If the file already exists, the user gets to choose whether
  1389.  *           to overwrite or append.
  1390.  */
  1391. void block_write( WINDOW *window )
  1392. {
  1393. int prompt_line;
  1394. int rc;
  1395. char buff[MAX_COLS+2]; /* buffer for char and attribute  */
  1396. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  1397. text_ptr block_start;   /* start of block in file */
  1398. text_ptr block_end;     /* end of block in file */
  1399. file_infos *file;
  1400. int block_type;
  1401.  
  1402.    /*
  1403.     * make sure block is marked OK
  1404.     */
  1405.    un_copy_line( window->cursor, window, TRUE );
  1406.    check_block( );
  1407.    if (g_status.marked == TRUE) {
  1408.       prompt_line = window->bottom_line;
  1409.       file        = g_status.marked_file;
  1410.       block_start = file->block_start;
  1411.       block_end   = file->block_end;
  1412.       block_type  = file->block_type;
  1413.  
  1414.       /*
  1415.        * find out which file to write to
  1416.        */
  1417.       save_screen_line( 0, prompt_line, line_buff );
  1418.       if (get_name( "Filename: ", prompt_line, g_status.rw_name,
  1419.                     g_display.message_color ) == OK) {
  1420.          /*
  1421.           * if the file exists, find out whether to overwrite or append
  1422.           */
  1423.          rc = hw_fattrib( g_status.rw_name );
  1424.          if (rc == OK) {
  1425.             set_prompt( "File exists. Overwrite or Append? (o/a): ",
  1426.                          prompt_line );
  1427.             switch (get_oa( )) {
  1428.                case A_OVERWRITE :
  1429.                   change_mode( g_status.rw_name, prompt_line );
  1430.                   combine_strings( buff, "writing block to '",
  1431.                                    g_status.rw_name, "'" );
  1432.                   s_output( buff, prompt_line, 0, g_display.message_color );
  1433.                   rc = hw_save( g_status.rw_name, block_start, block_end,
  1434.                                 block_type );
  1435.                   if (rc == ERROR)
  1436.                      error( WARNING, prompt_line, "could not write block" );
  1437.                   break;
  1438.                case A_APPEND :
  1439.                   combine_strings( buff, "appending block to '",
  1440.                                    g_status.rw_name, "'" );
  1441.                   s_output( buff, prompt_line, 0, g_display.message_color );
  1442.                   rc = hw_append( g_status.rw_name, block_start, block_end,
  1443.                                   block_type );
  1444.                   if (rc == ERROR)
  1445.                      error( WARNING, prompt_line, "could not append block" );
  1446.                   break;
  1447.             }
  1448.          } else if (rc != ERROR) {
  1449.             combine_strings( buff, "writing block to '", g_status.rw_name,
  1450.                              "'" );
  1451.             s_output( buff, prompt_line, 0, g_display.message_color );
  1452.             if (hw_save( g_status.rw_name, block_start, block_end,
  1453.                          block_type ) == ERROR)
  1454.                error( WARNING, prompt_line, "could not write block" );
  1455.          }
  1456.       }
  1457.       restore_screen_line( 0, prompt_line, line_buff );
  1458.    }
  1459. }
  1460.  
  1461.  
  1462. /*
  1463.  * Name:    block_print
  1464.  * Purpose: Print an entire file or the currently marked block.
  1465.  * Date:    June 5, 1991
  1466.  * Passed:  window: information required to access current window
  1467.  * Notes:   With the added Critical Error Handler routine, let's fflush
  1468.  *          the print buffer first.
  1469.  */
  1470. void block_print( WINDOW *window )
  1471. {
  1472. char answer[MAX_COLS];          /* entire file or just marked block? */
  1473. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  1474. int col, func;
  1475. int prompt_line;
  1476. text_ptr block_start;   /* start of block in file */
  1477. text_ptr block_end;     /* end of block in file */
  1478. file_infos *file;
  1479. int block_type;
  1480. char *p;
  1481. int len;
  1482. int bc, ec, last_c;
  1483. unsigned long lbegin, lend;
  1484. long l;
  1485. int color;
  1486.  
  1487.    color = g_display.message_color;
  1488.    un_copy_line( window->cursor, window, TRUE );
  1489.    prompt_line = window->bottom_line;
  1490.    save_screen_line( 0, prompt_line, line_buff );
  1491.    /*
  1492.     * print entire file or just marked block?
  1493.     */
  1494.    strcpy( answer, "Print file or block? (f/b): " );
  1495.    col = strlen( answer );
  1496.    s_output( answer, prompt_line, 0, color );
  1497.    eol_clear( col, prompt_line, g_display.text_color );
  1498.    xygoto( col, prompt_line );
  1499.    func = col = 0;
  1500.    while (col != 'f' && col != 'F' && col != 'b' && col != 'B' &&
  1501.           func != AbortCommand) {
  1502.       col = getkey( );
  1503.       func = getfunc( col );
  1504.       if (col == ESC)
  1505.          func = AbortCommand;
  1506.    }
  1507.    fflush( stdprn );
  1508.    if (ceh.flag == ERROR)
  1509.       func = AbortCommand;
  1510.    if (func != AbortCommand) {
  1511.       file = window->file_info;
  1512.       if (col == 'f' || col == 'F') {
  1513.          block_start = file->start_text;
  1514.          block_end   = cpb( file->end_text ) - 1;
  1515.          block_type  = NOTMARKED;
  1516.          l           = file->length;
  1517.       } else if (col == 'b' || col == 'B') {
  1518.          check_block( );
  1519.          if (g_status.marked == TRUE) {
  1520.             file        = g_status.marked_file;
  1521.             block_start = file->block_start;
  1522.             block_end   = file->block_end;
  1523.             block_type  = file->block_type;
  1524.             l           = file->block_er + 1l - file->block_br;
  1525.          } else
  1526.             col = AbortCommand;
  1527.       }
  1528.       if (block_type == LINE) {
  1529.          block_end = cpb( block_end );
  1530.          block_end = find_prev( block_end );
  1531.       }
  1532.  
  1533.       if (col != AbortCommand) {
  1534.          eol_clear( 0, prompt_line, color );
  1535.          s_output( "Printing line         of         Press ESC to cancel.",
  1536.                    prompt_line, 0, color );
  1537.          ltoa( l, answer, 10 );
  1538.          s_output( answer, prompt_line, 25, color );
  1539.          xygoto( 14, prompt_line );
  1540.          block_start = cpf( block_start );
  1541.          if (block_type == BOX || block_type == STREAM) {
  1542.             bc = file->block_bc;
  1543.             ec = file->block_ec;
  1544.             last_c = ec + 1 - bc;
  1545.          }
  1546.          p = g_status.line_buff;
  1547.          lend = ptoul( block_end );
  1548.          for (l=1,col=OK; ptoul( block_start ) <= lend && col == OK; l++) {
  1549.             ltoa( l, answer, 10 );
  1550.             s_output( answer, prompt_line, 14, color );
  1551.             g_status.copied = FALSE;
  1552.             if (block_type == BOX) {
  1553.                load_box_buff( p, block_start, bc, ec );
  1554.                *(p+last_c) = '\n';
  1555.                *(p+last_c+1) = CONTROL_Z;
  1556.             } else if (block_type == STREAM && l == 1) {
  1557.                len = linelen( block_start );
  1558.                lbegin = ptoul( block_start );
  1559.                block_start += bc < len ? bc : len;
  1560.                copy_line( block_start, prompt_line );
  1561.                if (lbegin == lend) {
  1562.                   if (len > ec) {
  1563.                      *(p+last_c) = '\n';
  1564.                      *(p+last_c+1) = CONTROL_Z;
  1565.                   }
  1566.                }
  1567.             } else if (block_type == STREAM && ptoul( block_start )==lend) {
  1568.                copy_line( block_start, prompt_line );
  1569.                if (linelen( block_start ) > ec) {
  1570.                   *(p+ec+1) = '\n';
  1571.                   *(p+ec+2) = CONTROL_Z;
  1572.                }
  1573.             } else
  1574.                copy_line( block_start, prompt_line );
  1575.             len = find_CONTROL_Z( p );
  1576.             if (fwrite( p, sizeof( char ), len, stdprn ) < (unsigned)len ||
  1577.                 ceh.flag == ERROR)
  1578.                col = ERROR;
  1579.             if (col != ERROR) {
  1580.                fputc( '\r', stdprn );
  1581.                if (ceh.flag == ERROR)
  1582.                   col = ERROR;
  1583.             }
  1584.             block_start = find_next( block_start );
  1585.             if (block_start == NULL)
  1586.                block_start = block_end + 1;
  1587.             if (col == OK && _bios_keybrd( mode.enh_kbd ? 0x11 : 0x01 )) {
  1588.                col = getkey( );
  1589.                col = col == ESC ? AbortCommand : getfunc( col );
  1590.                if (col != AbortCommand)
  1591.                   col = OK;
  1592.             }
  1593.          }
  1594.          g_status.copied = FALSE;
  1595.          if (ceh.flag != ERROR)
  1596.             fflush( stdprn );
  1597.       }
  1598.    }
  1599.    g_status.copied = FALSE;
  1600.    restore_screen_line( 0, prompt_line, line_buff );
  1601. }
  1602.  
  1603.  
  1604. /*
  1605.  * Name:    get_block_fill_char
  1606.  * Purpose: get the character to fill marked block.
  1607.  * Date:    June 5, 1991
  1608.  * Passed:  window: information required to access current window
  1609.  *          c: address of character to fill block
  1610.  */
  1611. int  get_block_fill_char( WINDOW *window, int *c )
  1612. {
  1613. char answer[MAX_COLS];
  1614. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  1615. register int col;
  1616. int prompt_line;
  1617. int rc;
  1618.  
  1619.    rc = OK;
  1620.    prompt_line = window->bottom_line;
  1621.    save_screen_line( 0, prompt_line, line_buff );
  1622.    strcpy( answer, "Enter character to fill block (ESC to exit): " );
  1623.    s_output( answer, prompt_line, 0, g_display.message_color );
  1624.    col = strlen( answer );
  1625.    eol_clear( col, prompt_line, g_display.text_color );
  1626.    xygoto( col, prompt_line );
  1627.    col = getkey( );
  1628.    if (col >= 256)
  1629.       rc = ERROR;
  1630.    else
  1631.       *c = col;
  1632.    restore_screen_line( 0, prompt_line, line_buff );
  1633.    return( rc );
  1634. }
  1635.  
  1636.  
  1637. /*
  1638.  * Name:    get_block_numbers
  1639.  * Purpose: get the starting number and increment
  1640.  * Date:    June 5, 1991
  1641.  * Passed:  window:    information required to access current window
  1642.  *          block_num: address of number to start numbering
  1643.  *          block_inc: address of number to add to block_num
  1644.  *          just:      left or right justify numbers in block?
  1645.  */
  1646. int  get_block_numbers( WINDOW *window, long *block_num, long *block_inc,
  1647.                         int *just )
  1648. {
  1649. char answer[MAX_COLS];
  1650. int prompt_line;
  1651. register int rc;
  1652. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  1653. register int col;
  1654.  
  1655.    prompt_line = window->bottom_line;
  1656.  
  1657.    /*
  1658.     * don't assume anything on starting number - start w/ null string.
  1659.     */
  1660.    answer[0] = '\0';
  1661.    rc = get_name( "Enter starting number: ", prompt_line, answer,
  1662.                    g_display.message_color );
  1663.    if (answer[0] == '\0')
  1664.       rc = ERROR;
  1665.    if (rc != ERROR) {
  1666.       *block_num = atol( answer );
  1667.  
  1668.       /*
  1669.        * assume increment is 1
  1670.        */
  1671.       answer[0] = '1';
  1672.       answer[1] = '\0';
  1673.       rc = get_name( "Enter increment: ", prompt_line, answer,
  1674.                       g_display.message_color );
  1675.       if (answer[0] == '\0')
  1676.          rc = ERROR;
  1677.       if (rc != ERROR) {
  1678.          *block_inc = atol( answer );
  1679.  
  1680.          /*
  1681.           * now, get left or right justification.  save contents of screen
  1682.           *  in a buffer, then write contents of buffer back to screen when
  1683.           *  we get through w/ justification.
  1684.           */
  1685.          save_screen_line( 0, prompt_line, line_buff );
  1686.          strcpy( answer, "Left or Right justify numbers in block? (l/r): " );
  1687.          s_output( answer, prompt_line, 0, g_display.message_color );
  1688.          col = strlen( answer );
  1689.          eol_clear( col, prompt_line, g_display.text_color );
  1690.          xygoto( col, prompt_line );
  1691.          rc = get_lr( );
  1692.          if (rc != ERROR) {
  1693.             *just = rc;
  1694.             rc = OK;
  1695.          }
  1696.          restore_screen_line( 0, prompt_line, line_buff );
  1697.       }
  1698.    }
  1699.  
  1700.    /*
  1701.     * if everything is everything then return code = OK.
  1702.     */
  1703.    return( rc );
  1704. }
  1705.  
  1706.  
  1707. /*
  1708.  * Name:    block_expand_tabs
  1709.  * Purpose: Expand tabs in a marked block.
  1710.  * Date:    June 5, 1991
  1711.  * Passed:  window:  pointer to current window
  1712.  * Notes:   Tabs are expanded using the current tab interval.
  1713.  *          Lines are checked to make sure they are not too long.
  1714.  */
  1715. void block_expand_tabs( WINDOW *window )
  1716. {
  1717. int prompt_line;
  1718. int len;
  1719. int tab;
  1720. int tab_size;
  1721. int dirty;
  1722. int spaces;
  1723. int net_change;
  1724. text_ptr p;                     /* pointer to block line */
  1725. file_infos *file;
  1726. WINDOW *sw, s_w;
  1727. long er;
  1728. int i;
  1729. char *b, *d, *lb;
  1730.  
  1731.    /*
  1732.     * make sure block is marked OK and that this is a LINE block
  1733.     */
  1734.    prompt_line = window->bottom_line;
  1735.    un_copy_line( window->cursor, window, TRUE );
  1736.    check_block( );
  1737.    if (g_status.marked == TRUE) {
  1738.  
  1739.       file  = g_status.marked_file;
  1740.       if (file->block_type != LINE) {
  1741.          error( WARNING, prompt_line,
  1742.                 "can only expand tabs in line blocks" );
  1743.          return;
  1744.       }
  1745.  
  1746.       /*
  1747.        * set the command to word wrap so the un_copy_line function will
  1748.        * not display the lines while expanding.
  1749.        */
  1750.       g_status.command = WordWrap;
  1751.  
  1752.       /*
  1753.        * initialize everything
  1754.        */
  1755.       dirty = FALSE;
  1756.       tab_size = mode.tab_size;
  1757.       sw = g_status.window_list;
  1758.       for (; ptoul( sw->file_info ) != ptoul( file );)
  1759.          sw = sw->next;
  1760.       dup_window_info( &s_w, sw );
  1761.       p  = cpf( file->block_start );
  1762.       er = file->block_er;
  1763.       lb = g_status.line_buff;
  1764.       s_w.rline = file->block_br;
  1765.       for (; s_w.rline<=er; s_w.rline++) {
  1766.  
  1767.          /*
  1768.           * use the line buffer to expand LINE blocks.
  1769.           */
  1770.          tab = FALSE;
  1771.          len = linelen( p );
  1772.          net_change = 0;
  1773.          g_status.copied = FALSE;
  1774.  
  1775.          copy_line( p, prompt_line );
  1776.          for (b=lb, i=1; *b != CONTROL_Z; b++) {
  1777.  
  1778.             /*
  1779.              * each line in the LINE block is copied to the g_status.line_buff.
  1780.              *  look at the text in the buffer and expand tabs.
  1781.              */
  1782.             if (*b == '\t') {
  1783.                tab = TRUE;
  1784.                spaces = i % tab_size;
  1785.                if (spaces)
  1786.                   spaces = tab_size - spaces;
  1787.                if (spaces) {
  1788.                   d = b + spaces;
  1789.                   memmove( d, b, linelen( b )+2 );
  1790.                }
  1791.                memset( b, ' ', spaces+1 );
  1792.                net_change += spaces;
  1793.                i += spaces + 1;
  1794.                b += spaces;
  1795.             } else
  1796.                i++;
  1797.          }
  1798.  
  1799.          /*
  1800.           * if any tabs were found, write g_status.line_buff to file.
  1801.           */
  1802.          if (tab) {
  1803.             un_copy_line( p, &s_w, TRUE );
  1804.             dirty = TRUE;
  1805.          }
  1806.          p = find_next( p );
  1807.       }
  1808.  
  1809.       /*
  1810.        * IMPORTANT:  we need to reset the copied flag because the cursor may
  1811.        * not necessarily be on the last line of the block.
  1812.        */
  1813.       g_status.copied = FALSE;
  1814.       if (dirty) {
  1815.          check_block( );
  1816.          file->dirty = GLOBAL;
  1817.       }
  1818.    }
  1819. }
  1820.  
  1821.  
  1822. /*
  1823.  * Name:    block_trim_trailing
  1824.  * Purpose: Trim trailing space in a LINE block.
  1825.  * Date:    June 5, 1991
  1826.  * Passed:  window:  pointer to current window
  1827.  * Notes:   Use copy_line and un_copy_line to do the work.
  1828.  */
  1829. void block_trim_trailing( WINDOW *window )
  1830. {
  1831. int prompt_line;
  1832. text_ptr p;                     /* pointer to block line */
  1833. file_infos *file;
  1834. WINDOW *sw, s_w;
  1835. long er;
  1836. int  trailing;               /* save trailing setting */
  1837.  
  1838.    /*
  1839.     * make sure block is marked OK and that this is a LINE block
  1840.     */
  1841.    prompt_line = window->bottom_line;
  1842.    un_copy_line( window->cursor, window, TRUE );
  1843.    check_block( );
  1844.    if (g_status.marked == TRUE) {
  1845.  
  1846.       trailing = mode.trailing;
  1847.       mode.trailing = TRUE;
  1848.       file = g_status.marked_file;
  1849.       if (file->block_type != LINE) {
  1850.          error( WARNING, prompt_line,
  1851.                 "can only trim trailing space in line blocks" );
  1852.          return;
  1853.       }
  1854.  
  1855.       /*
  1856.        * set the command to word wrap so the un_copy_line function will
  1857.        * not display the lines while trimming.
  1858.        */
  1859.       g_status.command = WordWrap;
  1860.  
  1861.       /*
  1862.        * initialize everything
  1863.        */
  1864.       sw = g_status.window_list;
  1865.       for (; ptoul( sw->file_info ) != ptoul( file );)
  1866.          sw = sw->next;
  1867.       dup_window_info( &s_w, sw );
  1868.       p  = cpf( file->block_start );
  1869.       er = file->block_er;
  1870.       s_w.rline = file->block_br;
  1871.       for (; s_w.rline<=er; s_w.rline++) {
  1872.  
  1873.          /*
  1874.           * use the line buffer to trim space.
  1875.           */
  1876.          copy_line( p, prompt_line );
  1877.          un_copy_line( p, &s_w, TRUE );
  1878.          p = find_next( p );
  1879.       }
  1880.  
  1881.       /*
  1882.        * IMPORTANT:  we need to reset the copied flag because the cursor may
  1883.        * not necessarily be on the last line of the block.
  1884.        */
  1885.       g_status.copied = FALSE;
  1886.       file->dirty = GLOBAL;
  1887.       mode.trailing = trailing;
  1888.    }
  1889. }
  1890.  
  1891.  
  1892. /*
  1893.  * Name:    block_convert_case
  1894.  * Purpose: convert characters to lower case, upper case, or strip hi bits
  1895.  * Date:    June 5, 1991
  1896.  * Passed:  window:  pointer to current window
  1897.  */
  1898. void block_convert_case( WINDOW *window )
  1899. {
  1900. int len;
  1901. int block_type;
  1902. text_ptr begin;
  1903. text_ptr end;                   /* pointer to block line */
  1904. register file_infos *file;
  1905. unsigned long number;
  1906. unsigned long er;
  1907. unsigned int count;
  1908. int bc, ec;
  1909. int block_len;
  1910. void (*char_func)( text_ptr, unsigned int );
  1911.  
  1912.    /*
  1913.     * make sure block is marked OK
  1914.     */
  1915.    un_copy_line( window->cursor, window, TRUE );
  1916.    check_block( );
  1917.    if (g_status.marked == TRUE) {
  1918.  
  1919.       /*
  1920.        * set char_func() to the required block function in tdeasm.c
  1921.        */
  1922.       switch (g_status.command) {
  1923.          case BlockUpperCase  :
  1924.             char_func = upper_asm;
  1925.             break;
  1926.          case BlockLowerCase  :
  1927.             char_func = lower_asm;
  1928.             break;
  1929.          case BlockStripHiBit :
  1930.             char_func = strip_asm;
  1931.             break;
  1932.       }
  1933.  
  1934.       file  = g_status.marked_file;
  1935.       block_type = file->block_type;
  1936.       bc = file->block_bc;
  1937.       ec = file->block_ec;
  1938.  
  1939.       begin  = cpf( file->block_start );
  1940.       end    = cpf( file->block_end );
  1941.  
  1942.       /*
  1943.        * if this is a LINE or STREAM block, process characters in
  1944.        *   chunks of 0xf000.
  1945.        */
  1946.       if (block_type == LINE || block_type == STREAM) {
  1947.          if (block_type == STREAM) {
  1948.             len = linelen( begin );
  1949.             begin += len < bc ? len : bc;
  1950.             len = linelen( end );
  1951.             end += len < ec ? len : ec + 1;
  1952.          }
  1953.          number = ptoul( end ) - ptoul( begin );
  1954.          count = 0xf000;
  1955.          begin = nptos( begin );
  1956.          while (number > count) {
  1957.             (*char_func)( begin, count );
  1958.             number -= count;
  1959.             begin = nptos( begin + count );
  1960.          }
  1961.          /*
  1962.           * now less than 0xf000 is left, so finish off the conversion
  1963.           */
  1964.          (*char_func)( begin, (unsigned)number );
  1965.  
  1966.       /*
  1967.        * For BOX blocks, process characters by lines
  1968.        */
  1969.       } else {
  1970.          begin = cpf( begin );
  1971.          er = file->block_er;
  1972.          block_len = ec + 1 - bc;
  1973.          for (number=file->block_br; number <= er; number++) {
  1974.             len = linelen( begin );
  1975.             if (len > bc) {
  1976.                count =  len >= ec ? block_len : len - bc;
  1977.                (*char_func)( begin+bc, count );
  1978.             }
  1979.             begin = find_next( begin );
  1980.          }
  1981.       }
  1982.  
  1983.       /*
  1984.        * IMPORTANT:  we need to reset the copied flag because the cursor may
  1985.        * not necessarily be on the last line of the block.
  1986.        */
  1987.       g_status.copied = FALSE;
  1988.       file->dirty = GLOBAL;
  1989.       file->modified = TRUE;
  1990.    }
  1991. }
  1992.