home *** CD-ROM | disk | FTP | other *** search
/ World of Shareware - Software Farm 2 / wosw_2.zip / wosw_2 / CPROG / TDE31.ZIP / BLOCK.C next >
C/C++ Source or Header  |  1993-08-29  |  92KB  |  2,829 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.  * In the DTE editor, Doug only supported functions for STREAM blocks.
  20.  *
  21.  * The block routines have been EXTENSIVELY rewritten.  This editor uses LINE,
  22.  * STREAM, and BOX blocks.  That is, one may mark entire lines, streams of
  23.  * characters, or column blocks.  Block operations are done in place.  There
  24.  * are no paste and cut buffers.  In limited memory situations, larger block
  25.  * operations can be carried out.  Block operations can be done within or
  26.  * across files.
  27.  *
  28.  * In TDE, version 1.1, I separated the BOX and LINE actions.
  29.  *
  30.  * In TDE, version 1.3, I put STREAM blocks back in.  Added block upper case,
  31.  *  block lower case, and block strip high bit.
  32.  *
  33.  * In TDE, version 1.4, I added a block number function.  Here at our lab,
  34.  *  I often need to number samples, lines, etc..., comes in fairly useful.
  35.  *
  36.  * In TDE, version 2.0, I added a box block sort function.
  37.  *
  38.  * In TDE, version 2.0e, I added BlockRot13, BlockFixUUE, and BlockEmailReply.
  39.  *
  40.  * In TDE, version 2.2, the big text buffer was changed to a double linked list.
  41.  *  all characters in the line of each node must be accurately counted.
  42.  *
  43.  * New editor name:  TDE, the Thomson-Davis Editor.
  44.  * Author:           Frank Davis
  45.  * Date:             June 5, 1991, version 1.0
  46.  * Date:             July 29, 1991, version 1.1
  47.  * Date:             October 5, 1991, version 1.2
  48.  * Date:             January 20, 1992, version 1.3
  49.  * Date:             February 17, 1992, version 1.4
  50.  * Date:             April 1, 1992, version 1.5
  51.  * Date:             June 5, 1992, version 2.0
  52.  * Date:             October 31, 1992, version 2.1
  53.  * Date:             April 1, 1993, version 2.2
  54.  * Date:             June 5, 1993, version 3.0
  55.  * Date:             August 29, 1993, version 3.1
  56.  *
  57.  * This modification of Douglas Thomson's code is released into the
  58.  *  public domain, Frank Davis.  You may distribute it freely.
  59.  */
  60.  
  61. #include "tdestr.h"
  62. #include "common.h"
  63. #include "tdefunc.h"
  64. #include "define.h"
  65.  
  66.  
  67. /*
  68.  * Name:    mark_block
  69.  * Class:   primary editor function
  70.  * Purpose: To record the position of the start of the block in the file.
  71.  * Date:    June 5, 1991
  72.  * Passed:  window:  pointer to current window
  73.  * Notes:   Assume the user will mark begin and end of a block in either
  74.  *           line, stream, or box mode.  If the user mixes types, then block
  75.  *           type defaults to current block type.
  76.  */
  77. int  mark_block( WINDOW *window )
  78. {
  79. int type;
  80. int num;
  81. long lnum;
  82. register file_infos *file;      /* temporary file variable */
  83. register WINDOW *win;           /* put window pointer in a register */
  84. int rc;
  85.  
  86.    win  = window;
  87.    file = win->file_info;
  88.    if (win->rline > file->length || win->ll->len == EOF)
  89.       return( ERROR );
  90.    if (g_status.marked == FALSE) {
  91.       g_status.marked = TRUE;
  92.       g_status.marked_file = file;
  93.    }
  94.    if (g_status.command == MarkBox)
  95.       type = BOX;
  96.    else if (g_status.command == MarkLine)
  97.       type = LINE;
  98.    else if (g_status.command == MarkStream)
  99.       type = STREAM;
  100.    else
  101.       return( ERROR );
  102.  
  103.    rc = OK;
  104.    /*
  105.     * define blocks for only one file.  it is ok to modify blocks in any window
  106.     * pointing to original marked file.
  107.     */
  108.    if (file == g_status.marked_file) {
  109.  
  110.       /*
  111.        * mark beginning and ending column regardless of block mode.
  112.        */
  113.       if (file->block_type == NOTMARKED) {
  114.          file->block_ec  = file->block_bc = win->rcol;
  115.          file->block_er  = file->block_br = win->rline;
  116.       } else {
  117.          if (file->block_br > win->rline) {
  118.             file->block_br = win->rline;
  119.             if (file->block_bc < win->rcol && type != STREAM)
  120.                file->block_ec = win->rcol;
  121.             else
  122.                file->block_bc = win->rcol;
  123.          } else {
  124.             if (type != STREAM) {
  125.                file->block_ec = win->rcol;
  126.                file->block_er = win->rline;
  127.             } else {
  128.                if (win->rline == file->block_br &&
  129.                    win->rline == file->block_er) {
  130.                   if (win->rcol < file->block_bc)
  131.                      file->block_bc = win->rcol;
  132.                   else
  133.                      file->block_ec = win->rcol;
  134.                } else if (win->rline == file->block_br)
  135.                   file->block_bc = win->rcol;
  136.                else {
  137.                   file->block_ec = win->rcol;
  138.                   file->block_er = win->rline;
  139.                }
  140.             }
  141.          }
  142.  
  143.          /*
  144.           * if user marks ending line less than beginning line then switch
  145.           */
  146.          if (file->block_er < file->block_br) {
  147.             lnum = file->block_er;
  148.             file->block_er = file->block_br;
  149.             file->block_br = lnum;
  150.          }
  151.  
  152.          /*
  153.           * if user marks ending column less than beginning column then switch
  154.           */
  155.          if ((file->block_ec < file->block_bc) && (type != STREAM ||
  156.               (type == STREAM && file->block_br == file->block_er))) {
  157.             num = file->block_ec;
  158.             file->block_ec = file->block_bc;
  159.             file->block_bc = num;
  160.          }
  161.       }
  162.  
  163.       /*
  164.        * block type in now defined.  if user mixes block types then block
  165.        * is defined as current block type.
  166.        */
  167.       if (file->block_type != NOTMARKED) {
  168.          /*
  169.           * if block type goes to BOX, check to make sure ec is greater than
  170.           * or equal to bc.  ec can be less than bc in STREAM blocks.
  171.           */
  172.          if (type == BOX) {
  173.             if (file->block_ec < file->block_bc) {
  174.                num = file->block_ec;
  175.                file->block_ec = file->block_bc;
  176.                file->block_bc = num;
  177.             }
  178.          }
  179.       }
  180.  
  181.       assert( file->block_er >= file->block_br );
  182.  
  183.       file->block_type = type;
  184.       file->dirty = GLOBAL;
  185.    } else {
  186.       /*
  187.        * block already defined
  188.        */
  189.       error( WARNING, win->bottom_line, block1 );
  190.       rc = ERROR;
  191.    }
  192.    return( rc );
  193. }
  194.  
  195.  
  196. /*
  197.  * Name:    unmark_block
  198.  * Class:   primary editor function
  199.  * Purpose: To set all block information to NULL or 0
  200.  * Date:    June 5, 1991
  201.  * Passed:  arg_filler: variable to match array of function pointers prototype
  202.  * Notes:   Reset all block variables if marked, otherwise return.
  203.  *           If a marked block is unmarked then redraw the screen(s).
  204.  */
  205. int  unmark_block( WINDOW *arg_filler )
  206. {
  207. register file_infos *marked_file;
  208.  
  209.    if (g_status.marked == TRUE) {
  210.       marked_file              = g_status.marked_file;
  211.       g_status.marked          = FALSE;
  212.       g_status.marked_file     = NULL;
  213.       marked_file->block_start = NULL;
  214.       marked_file->block_end   = NULL;
  215.       marked_file->block_bc    = marked_file->block_ec = 0;
  216.       marked_file->block_br    = marked_file->block_er = 0l;
  217.       if (marked_file->block_type)
  218.          marked_file->dirty = GLOBAL;
  219.       marked_file->block_type  = NOTMARKED;
  220.    }
  221.    return( OK );
  222. }
  223.  
  224.  
  225. /*
  226.  * Name:    restore_marked_block
  227.  * Class:   helper function
  228.  * Purpose: To restore block beginning and ending row after an editing function
  229.  * Date:    June 5, 1991
  230.  * Passed:  window:  pointer to current window
  231.  *          net_change: number of bytes added or subtracted
  232.  * Notes:   If a change has been made before the marked block then the
  233.  *           beginning and ending row need to be adjusted by the number of
  234.  *           lines added or subtracted from file.
  235.  */
  236. void restore_marked_block( WINDOW *window, int net_change )
  237. {
  238. long length;
  239. register file_infos *marked_file;
  240.  
  241.    if (g_status.marked == TRUE && net_change != 0) {
  242.       marked_file = g_status.marked_file;
  243.       length = marked_file->length;
  244.  
  245.       /*
  246.        * restore is needed only if a block is defined and window->file_info is
  247.        * same as marked file and there was a net change in file length.
  248.        */
  249.       if (marked_file == window->file_info) {
  250.  
  251.          /*
  252.           * if cursor is before marked block then adjust block by net change.
  253.           */
  254.          if (marked_file->block_br > window->rline) {
  255.             marked_file->block_br += net_change;
  256.             marked_file->block_er += net_change;
  257.             marked_file->dirty = GLOBAL;
  258.          /*
  259.           * if cursor is somewhere in marked block don't restore, do redisplay
  260.           */
  261.          } else if (marked_file->block_er >= window->rline)
  262.             marked_file->dirty = GLOBAL;
  263.  
  264.          /*
  265.           * check for lines of marked block beyond end of file
  266.           */
  267.          if (marked_file->block_br > length)
  268.             unmark_block( window );
  269.          else if (marked_file->block_er > length) {
  270.             marked_file->block_er = length;
  271.             marked_file->dirty = GLOBAL;
  272.          }
  273.       }
  274.    }
  275. }
  276.  
  277.  
  278. /*
  279.  * Name:    prepare_block
  280.  * Class:   helper function
  281.  * Purpose: To prepare a window/file for a block read, move or copy.
  282.  * Date:    June 5, 1991
  283.  * Passed:  window:  pointer to current window
  284.  *          file: pointer to file information.
  285.  *          text_line: pointer to line in file to prepare.
  286.  *          lend: line length.
  287.  *          bc: beginning column of BOX.
  288.  * Notes:   The main complication is that the cursor may be beyond the end
  289.  *           of the current line, in which case extra padding spaces have
  290.  *           to be added before the block operation can take place.
  291.  *           this only occurs in BOX and STREAM operations.
  292.  *          since we are padding a line, do not trim trailing space.
  293.  */
  294. int  prepare_block( WINDOW *window, line_list_ptr ll, int bc )
  295. {
  296. register int pad = 0;   /* amount of padding to be added */
  297. register int len;
  298.  
  299.    assert( bc >= 0 );
  300.    assert( bc < MAX_LINE_LENGTH );
  301.    assert( ll->len != EOF );
  302.    assert( g_status.copied == FALSE );
  303.  
  304.    copy_line( ll );
  305.    detab_linebuff( );
  306.  
  307.    len = g_status.line_buff_len;
  308.    pad = bc - len;
  309.    if (pad > 0) {
  310.       /*
  311.        * insert the padding spaces
  312.        */
  313.  
  314.       assert( pad >= 0 );
  315.       assert( pad < MAX_LINE_LENGTH );
  316.  
  317.       memset( g_status.line_buff+len, ' ', pad );
  318.       g_status.line_buff_len += pad;
  319.    }
  320.    /*
  321.     * if mode.inflate_tabs, let's don't entab the line until we get
  322.     *   thru processing this line, e.g. copying, numbering....
  323.     */
  324.    return( un_copy_line( ll, window, FALSE ) );
  325. }
  326.  
  327.  
  328. /*
  329.  * Name:    pad_dest_line
  330.  * Class:   helper function
  331.  * Purpose: To prepare a window/file for a block move or copy.
  332.  * Date:    June 5, 1991
  333.  * Passed:  window:  pointer to current window
  334.  *          dest_file: pointer to file information.
  335.  *          dest_line: pointer to line in file to prepare.
  336.  *          ll:        pointer to linked list node to insert new node
  337.  * Notes:   We are doing a BOX action (except DELETE).   We have come
  338.  *          to the end of the file and have no more lines.  All this
  339.  *          routine does is add a blank line to file.
  340.  */
  341. int  pad_dest_line( WINDOW *window, file_infos *dest_file, line_list_ptr ll )
  342. {
  343. int rc;
  344. text_ptr l;
  345. line_list_ptr new_node;
  346.  
  347.    rc = OK;
  348.  
  349.    l = NULL;
  350.    new_node = (line_list_ptr)my_malloc( sizeof(line_list_struc), &rc );
  351.    if (rc == OK) {
  352.       new_node->len   = 0;
  353.       new_node->dirty = FALSE;
  354.       new_node->line  = l;
  355.       if (ll->next != NULL) {
  356.          ll->next->prev = new_node;
  357.          new_node->next = ll->next;
  358.          ll->next = new_node;
  359.          new_node->prev = ll;
  360.       } else {
  361.          new_node->next = ll;
  362.          if (ll->prev != NULL)
  363.             ll->prev->next = new_node;
  364.          new_node->prev = ll->prev;
  365.          ll->prev = new_node;
  366.          if (new_node->prev == NULL)
  367.             window->file_info->line_list = new_node;
  368.       }
  369.       ++dest_file->length;
  370.    } else {
  371.       /*
  372.        * file too big
  373.        */
  374.       error( WARNING, window->bottom_line, block4 );
  375.       if (new_node != NULL)
  376.          my_free( new_node );
  377.       rc = ERROR;
  378.    }
  379.    return( rc );
  380. }
  381.  
  382.  
  383. /*
  384.  * Name:    move_copy_delete_overlay_block
  385.  * Class:   primary editor function
  386.  * Purpose: Master BOX, STREAM, or LINE routine.
  387.  * Date:    June 5, 1991
  388.  * Passed:  window:  pointer to current window
  389.  * Notes:   Operations on BOXs, STREAMs, or LINEs require several common
  390.  *           operations.  All require finding the beginning and ending marks.
  391.  *          This routine will handle block operations across files.  Since one
  392.  *           must determine the relationship of source and destination blocks
  393.  *           within a file, it is relatively easy to expand this relationship
  394.  *           across files.
  395.  *          This is probably the most complicated routine in the editor.  It
  396.  *           is not easy to understand.
  397.  */
  398. int  move_copy_delete_overlay_block( WINDOW *window )
  399. {
  400. int  action;
  401. WINDOW *source_window;          /* source window for block moves */
  402. line_list_ptr source;           /* source for block moves */
  403. line_list_ptr dest;             /* destination for block moves */
  404. long number;                    /* number of characters for block moves */
  405. int  lens;                      /* length of source line */
  406. int  lend;                      /* length of destination line */
  407. int  add;                       /* characters being added from another line */
  408. int  block_len;                 /* length of the block */
  409. line_list_ptr block_start;      /* start of block in file */
  410. line_list_ptr block_end;  /* end of block in file - not same for LINE or BOX */
  411. int  prompt_line;
  412. int  same;                      /* are dest and source files the same file? */
  413. int  source_first;              /* is source file lower in memory than dest */
  414. file_infos *source_file;
  415. file_infos *dest_file;
  416. int  rcol, bc, ec;              /* temporary column variables */
  417. long rline;                     /* temporary real line variable */
  418. long br, er;                    /* temporary line variables */
  419. long block_num;                 /* starting number for block number */
  420. long block_inc;                 /* increment to use for block number */
  421. int  block_just;                /* left or right justify numbers? */
  422. int  block_type;
  423. int  fill_char;
  424. int  rc;
  425.  
  426.    /*
  427.     * initialize block variables
  428.     */
  429.    entab_linebuff( );
  430.    rc = un_copy_line( window->ll, window, TRUE );
  431.    if (g_status.marked == FALSE || rc == ERROR)
  432.       return( ERROR );
  433.    switch (g_status.command) {
  434.       case MoveBlock :
  435.          action = MOVE;
  436.          break;
  437.       case DeleteBlock :
  438.          action = DELETE;
  439.          break;
  440.       case CopyBlock :
  441.          action = COPY;
  442.          break;
  443.       case KopyBlock :
  444.          action = KOPY;
  445.          break;
  446.       case FillBlock :
  447.          action = FILL;
  448.          break;
  449.       case OverlayBlock :
  450.          action = OVERLAY;
  451.          break;
  452.       case NumberBlock :
  453.          action = NUMBER;
  454.          break;
  455.       case SwapBlock :
  456.          action = SWAP;
  457.          break;
  458.       default :
  459.          return( ERROR );
  460.    }
  461.    source_file = g_status.marked_file;
  462.    source_window = g_status.window_list;
  463.    for (; ptoul( source_window->file_info ) != ptoul( source_file );)
  464.       source_window = source_window->next;
  465.    prompt_line = window->bottom_line;
  466.    dest_file = window->file_info;
  467.    check_block( );
  468.    if (g_status.marked == FALSE)
  469.       return( ERROR );
  470.    block_start = source_file->block_start;
  471.    block_end = source_file->block_end;
  472.    if (block_start == NULL  ||  block_end == NULL)
  473.       return( ERROR );
  474.  
  475.    block_type = source_file->block_type;
  476.    if (block_type != LINE  &&  block_type != STREAM  &&  block_type != BOX)
  477.       return( ERROR );
  478.  
  479.    dest = window->ll;
  480.    rline = window->rline;
  481.    if (dest->len == EOF)
  482.       return( ERROR );
  483.    rc = OK;
  484.  
  485.    /*
  486.     * set up Beginning Column, Ending Column, Beginning Row, Ending Row
  487.     */
  488.    bc = source_file->block_bc;
  489.    ec = source_file->block_ec;
  490.    br = source_file->block_br;
  491.    er = source_file->block_er;
  492.  
  493.    /*
  494.     * if we are BOX FILLing or BOX NUMBERing, beginning column is bc,
  495.     *   not the column of cursor
  496.     */
  497.    rcol =  (action == FILL || action == NUMBER) ? bc : window->rcol;
  498.  
  499.    /*
  500.     * must find out if source and destination file are the same.
  501.     * it don't matter with FILL and DELETE - those actions only modify the
  502.     * source file.
  503.     */
  504.    source_first = same = FALSE;
  505.    if (action == FILL) {
  506.       if (block_type == BOX) {
  507.          if (get_block_fill_char( window, &fill_char ) == ERROR)
  508.             return( ERROR );
  509.          dest = block_start;
  510.          same = TRUE;
  511.       } else {
  512.          /*
  513.           * can only fill box blocks.
  514.           */
  515.          error( WARNING, prompt_line, block2 );
  516.          return( ERROR );
  517.       }
  518.    }
  519.    block_inc = 1;
  520.    if (action == NUMBER) {
  521.       if (block_type == BOX) {
  522.          if (get_block_numbers( window, &block_num, &block_inc, &block_just )
  523.               == ERROR)
  524.             return( ERROR );
  525.          dest = block_start;
  526.          same = TRUE;
  527.       } else {
  528.          /*
  529.           * can only number box blocks.
  530.           */
  531.          error( WARNING, prompt_line, block3a );
  532.          return( ERROR );
  533.       }
  534.    }
  535.    if (action == SWAP) {
  536.       if (block_type != BOX) {
  537.          /*
  538.           * can only swap box blocks.
  539.           */
  540.          error( WARNING, prompt_line, block3b );
  541.          return( ERROR );
  542.       }
  543.    }
  544.    if (source_file == dest_file && action != DELETE && action != FILL) {
  545.       same = TRUE;
  546.       if (block_type == BOX && action == MOVE) {
  547.          if (rline == br  &&  (rcol >= bc && rcol <= ec))
  548.             /*
  549.              * a block moved to within the block itself has no effect
  550.              */
  551.             return( ERROR );
  552.       } else if (block_type == LINE || block_type == STREAM) {
  553.          if (rline >= br && rline <= er) {
  554.             if (block_type == LINE) {
  555.                 /*
  556.                  * if COPYing or KOPYing within the block itself, reposition the
  557.                  * destination to the next line after the block (if it exists)
  558.                  */
  559.                if (action == COPY || action == KOPY)
  560.                   dest = block_end;
  561.                 /*
  562.                  * a block moved to within the block itself has no effect
  563.                  */
  564.                else if (action == MOVE)
  565.                   return( ERROR );
  566.             } else {
  567.  
  568.                /*
  569.                 * to find out if cursor is in a STREAM block we have to do
  570.                 * a few more tests.  if cursor is on the beginning row or
  571.                 * ending row, then check the beginning and ending column.
  572.                 */
  573.                if ((rline > br && rline < er) ||
  574.                    (br == er && rcol >= bc && rcol <= ec) ||
  575.                    (br != er && ((rline == br && rcol >= bc) ||
  576.                                  (rline == er && rcol <= ec)))) {
  577.  
  578.                   /*
  579.                    * if the cursor is in middle of STREAM, make destination
  580.                    * the last character following the STREAM block.
  581.                    */
  582.                   if (action == COPY || action == KOPY) {
  583.                      dest = block_end;
  584.                      rcol = ec + 1;
  585.                      rline = er;
  586.                   } else if (action == MOVE)
  587.                      return( ERROR );
  588.                }
  589.             }
  590.          }
  591.       }
  592.    }
  593.    if (br < rline)
  594.       source_first = TRUE;
  595.  
  596.    /*
  597.     * 1. can't create lines greater than g_display.line_length
  598.     * 2. if we are FILLing a BOX - fill block buff once right here
  599.     * 3. only allow overlaying BOXs
  600.     */
  601.    block_len = (ec+1) - bc;
  602.    if (block_type == BOX) {
  603.       if (action != DELETE && action != FILL) {
  604.          if (rcol + block_len > MAX_LINE_LENGTH) {
  605.             /*
  606.              * line too long
  607.              */
  608.             error( WARNING, prompt_line, ltol );
  609.             return( ERROR );
  610.          }
  611.       }
  612.    } else if (block_type == LINE) {
  613.       block_len = 0;
  614.       if (action == OVERLAY) {
  615.          /*
  616.           * can only overlay box blocks
  617.           */
  618.          error( WARNING, prompt_line, block5 );
  619.          return( ERROR );
  620.       }
  621.    } else if (block_type == STREAM) {
  622.  
  623.       if (action == OVERLAY) {
  624.          /*
  625.           * can only overlay box blocks
  626.           */
  627.          error( WARNING, prompt_line, block5 );
  628.          return( ERROR );
  629.       }
  630.  
  631.       lend = block_end->len;
  632.       if (action == DELETE || action == MOVE) {
  633.  
  634.          /*
  635.           * Is what's left on start of STREAM block line plus what's left at
  636.           * end of STREAM block line too long?
  637.           */
  638.          if (lend > ec)
  639.             lend -= ec;
  640.          else
  641.             lend = 0;
  642.          if (bc + lend > MAX_LINE_LENGTH) {
  643.             /*
  644.              * line too long
  645.              */
  646.             error( WARNING, prompt_line, ltol );
  647.             return( ERROR );
  648.          }
  649.       }
  650.  
  651.       if (action != DELETE) {
  652.  
  653.          /*
  654.           * We are doing a MOVE, COPY, or KOPY.  Find out if what's on the
  655.           * current line plus the start of the STREAM line are too long.
  656.           * Then find out if end of the STREAM line plus what's left of
  657.           * the current line are too long.
  658.           */
  659.          lens = block_start->len;
  660.  
  661.          /*
  662.           * if we had to move the destination of the STREAM COPY or KOPY
  663.           * to the end of the STREAM block, then dest and window->ll->line
  664.           * will not be the same.  In this case, set length to length of
  665.           * first line in STREAM block.  Then we can add the residue of
  666.           * the first line in block plus residue of the last line of block.
  667.           */
  668.          if (dest->line == window->ll->line)
  669.             add = dest->len;
  670.          else
  671.             add = lens;
  672.  
  673.          /*
  674.           * Is current line plus start of STREAM block line too long?
  675.           */
  676.          if (lens > bc)
  677.             lens -= bc;
  678.          else
  679.             lens = 0;
  680.          if (rcol + lens > MAX_LINE_LENGTH) {
  681.             /*
  682.              * line too long
  683.              */
  684.             error( WARNING, prompt_line, ltol );
  685.             return( ERROR );
  686.          }
  687.  
  688.          /*
  689.           * Is residue of current line plus residue of STREAM block line
  690.           * too long?
  691.           */
  692.          if (add > bc)
  693.             add -= bc;
  694.          else
  695.             add = 0;
  696.          if (lend > ec)
  697.             lend -= ec;
  698.          else
  699.             lend = 0;
  700.          if (add + lend > MAX_LINE_LENGTH) {
  701.             /*
  702.              * line too long
  703.              */
  704.             error( WARNING, prompt_line, ltol );
  705.             return( ERROR );
  706.          }
  707.       }
  708.       if (ptoul( block_start ) == ptoul( block_end )) {
  709.          block_type = BOX;
  710.          block_len = (ec+1) - bc;
  711.       }
  712.    }
  713.  
  714.    if (mode.do_backups == TRUE) {
  715.       switch (action) {
  716.          case MOVE :
  717.          case DELETE :
  718.          case COPY :
  719.          case KOPY :
  720.          case SWAP :
  721.             window->file_info->modified = TRUE;
  722.             rc = backup_file( window );
  723.             break;
  724.       }
  725.       switch (action) {
  726.          case MOVE :
  727.          case DELETE :
  728.          case FILL :
  729.          case NUMBER :
  730.          case SWAP :
  731.             source_window->file_info->modified = TRUE;
  732.             if (rc != ERROR)
  733.                rc = backup_file( source_window );
  734.             break;
  735.       }
  736.    }
  737.    source = block_start;
  738.  
  739.    assert( block_start != NULL );
  740.    assert( block_start->len != EOF );
  741.    assert( block_end != NULL );
  742.    assert( block_end->len != EOF );
  743.  
  744.    if (block_type == LINE)
  745.       do_line_block( window,  source_window,  action,
  746.                      source_file,  dest_file,  block_start,  block_end,
  747.                      source,  dest,  br,  er,  &rc );
  748.  
  749.    else if (block_type == STREAM)
  750.       do_stream_block( window,  source_window,  action,
  751.                        source_file,  dest_file,  block_start,  block_end,
  752.                        source,  dest,  rline,  br,  er,  bc,  ec,  rcol,  &rc );
  753.  
  754.    else
  755.       do_box_block( window,  source_window,  action,
  756.                     source_file,  dest_file,  source,  dest,  br,  er,
  757.                     block_inc, rline, block_num, block_just, fill_char,
  758.                     same, block_len, bc, ec,  rcol, &rc );
  759.  
  760.    dest_file->modified = TRUE;
  761.    dest_file->dirty = GLOBAL;
  762.    if (action == MOVE || action == DELETE || action == FILL || action==NUMBER) {
  763.       source_file->modified = TRUE;
  764.       source_file->dirty = GLOBAL;
  765.    }
  766.  
  767.    /*
  768.     * unless we are doing a KOPY, FILL, NUMBER, or OVERLAY we need to unmark
  769.     * the block.  if we just did a KOPY, the beginning and ending may have
  770.     * changed.  so, we must readjust beginning and ending rows.
  771.     */
  772.    if (action == KOPY) {
  773.       if (same && !source_first && block_type == LINE  &&  rc != ERROR) {
  774.          number = (er+1) - br;
  775.          source_file->block_br += number;
  776.          source_file->block_er += number;
  777.       }
  778.    } else if (action != FILL && action != OVERLAY && action != NUMBER)
  779.       unmark_block( window );
  780.    show_avail_mem( );
  781.    g_status.copied = FALSE;
  782.    return( rc );
  783. }
  784.  
  785.  
  786. /*
  787.  * Name:    do_line_block
  788.  * Purpose: delete, move, copy, or kopy a LINE block
  789.  * Date:    April 1, 1993
  790.  * Passed:  window:  pointer to current window
  791.  * Passed:  window:         pointer to destination window (current window)
  792.  *          source_window:  pointer to source window
  793.  *          action:         block action  --  KOPY, MOVE, etc...
  794.  *          source_file:    pointer to source file structure
  795.  *          dest_file:      pointer to destination file
  796.  *          block_start:    pointer to first node in block
  797.  *          block_end:      pointer to last node in block
  798.  *          source:         pointer to source node
  799.  *          dest:           pointer to destination node
  800.  *          br:             beginning line number in marked block
  801.  *          er:             ending line number in marked block
  802.  *          rc:             return code
  803.  */
  804. void do_line_block( WINDOW *window,  WINDOW *source_window,  int action,
  805.                     file_infos *source_file,  file_infos *dest_file,
  806.                     line_list_ptr block_start,  line_list_ptr block_end,
  807.                     line_list_ptr source,  line_list_ptr dest,
  808.                     long br,  long er, int *rc )
  809. {
  810. line_list_ptr temp_ll;          /* temporary list pointer */
  811. text_ptr l;
  812. int  lens;                      /* length of source line */
  813. long li;                        /* temporary line variables */
  814. long diff;
  815.  
  816.    if (action == COPY || action == KOPY) {
  817.  
  818.       assert( br >= 1 );
  819.       assert( br <= source_file->length );
  820.       assert( er >= br );
  821.       assert( er <= source_file->length );
  822.  
  823.       for (li=br; li <= er  &&  *rc == OK; li++) {
  824.          lens = source->len;
  825.  
  826.          assert( lens * sizeof(char) < MAX_LINE_LENGTH );
  827.  
  828.          l = (text_ptr)my_malloc( lens * sizeof(char), rc );
  829.          temp_ll = (line_list_ptr)my_malloc( sizeof(line_list_struc), rc );
  830.          if (*rc == OK) {
  831.             if (lens > 0)
  832.                _fmemcpy( l, source->line, lens );
  833.             temp_ll->line  = l;
  834.             temp_ll->len   = lens;
  835.             temp_ll->dirty = TRUE;
  836.  
  837.             if (dest->next != NULL) {
  838.                dest->next->prev = temp_ll;
  839.                temp_ll->next = dest->next;
  840.                dest->next = temp_ll;
  841.                temp_ll->prev = dest;
  842.             } else {
  843.                temp_ll->next = dest;
  844.                if (dest->prev != NULL)
  845.                   dest->prev->next = temp_ll;
  846.                temp_ll->prev = dest->prev;
  847.                dest->prev = temp_ll;
  848.                if (temp_ll->prev == NULL)
  849.                   window->file_info->line_list = temp_ll;
  850.             }
  851.  
  852.             dest = temp_ll;
  853.             source = source->next;
  854.          } else {
  855.             /*
  856.              * file too big
  857.              */
  858.             error( WARNING, window->bottom_line, dir3 );
  859.             if (l != NULL)
  860.                my_free( l );
  861.             if (temp_ll != NULL)
  862.                my_free( temp_ll );
  863.             *rc = ERROR;
  864.             er = li - 1;
  865.          }
  866.       }
  867.    } else if (action == MOVE) {
  868.       if (dest->len != EOF  &&  dest->next != NULL) {
  869.          temp_ll = block_start;
  870.          for (li=br; li <= er  &&  *rc == OK; li++) {
  871.             temp_ll->dirty = TRUE;
  872.             temp_ll = temp_ll->next;
  873.          }
  874.          if (block_start->prev == NULL)
  875.             source_file->line_list = block_end->next;
  876.          if (block_start->prev != NULL)
  877.             block_start->prev->next = block_end->next;
  878.          block_end->next->prev = block_start->prev;
  879.          dest->next->prev = block_end;
  880.          block_start->prev = dest;
  881.          block_end->next = dest->next;
  882.          dest->next = block_start;
  883.       }
  884.    } else if (action == DELETE) {
  885.       block_end->next->prev = block_start->prev;
  886.       if (block_start->prev == NULL)
  887.          source_file->line_list = block_end->next;
  888.       else
  889.          block_start->prev->next = block_end->next;
  890.       block_end->next = NULL;
  891.       while (block_start != NULL) {
  892.          temp_ll = block_start;
  893.          block_start = block_start->next;
  894.          if (temp_ll->line != NULL)
  895.             my_free( temp_ll->line );
  896.          my_free( temp_ll );
  897.       }
  898.    }
  899.  
  900.    diff =  er + 1L - br;
  901.    if (action == COPY || action == KOPY || action == MOVE)
  902.       dest_file->length += diff;
  903.    if (action == DELETE || action == MOVE)
  904.       source_file->length -= diff;
  905.    if (action == DELETE && source_window->rline >= br) {
  906.       source_window->rline -= diff;
  907.       if (source_window->rline < br)
  908.          source_window->rline = br;
  909.    }
  910.    /*
  911.     * restore all cursors in all windows
  912.     */
  913.    restore_cursors( dest_file );
  914.    if (dest_file != source_file)
  915.       restore_cursors( source_file );
  916.    show_avail_mem( );
  917. }
  918.  
  919.  
  920. /*
  921.  * Name:    do_stream_block
  922.  * Purpose: delete, move, copy, or kopy a STREAM block
  923.  * Date:    June 5, 1991
  924.  * Passed:  window:         pointer to destination window (current window)
  925.  *          source_window:  pointer to source window
  926.  *          action:         block action  --  KOPY, MOVE, etc...
  927.  *          source_file:    pointer to source file structure
  928.  *          dest_file:      pointer to destination file
  929.  *          block_start:    pointer to first node in block
  930.  *          block_end:      pointer to last node in block
  931.  *          source:         pointer to source node
  932.  *          dest:           pointer to destination node
  933.  *          rline:          current line number in destination file
  934.  *          br:             beginning line number in marked block
  935.  *          er:             ending line number in marked block
  936.  *          bc:             beginning column of block
  937.  *          ec:             ending column of block
  938.  *          rcol:           current column of cursor
  939.  *          rc:             return code
  940.  */
  941. void do_stream_block( WINDOW *window,  WINDOW *source_window,  int action,
  942.                     file_infos *source_file,  file_infos *dest_file,
  943.                     line_list_ptr block_start,  line_list_ptr block_end,
  944.                     line_list_ptr source,  line_list_ptr dest, long rline,
  945.                     long br,  long er, int bc, int ec, int rcol, int *rc )
  946. {
  947. line_list_ptr temp_ll;          /* temporary list pointer */
  948. text_ptr l;
  949. int  lens;                      /* length of source line */
  950. int  lend;                      /* length of destination line */
  951. long li;                        /* temporary line variables */
  952. long diff;
  953. WINDOW s_w, d_w;                /* a couple of temporary WINDOWs */
  954.  
  955.    dup_window_info( &s_w, source_window );
  956.    dup_window_info( &d_w, window );
  957.    s_w.rline   = br;
  958.    s_w.ll      = block_start;
  959.    s_w.visible = FALSE;
  960.    d_w.rline   = rline;
  961.    d_w.ll      = dest;
  962.    d_w.visible = FALSE;
  963.  
  964.    /*
  965.     * pad the start of the STREAM block if needed.
  966.     */
  967.    lens = block_start->len;
  968.    detab_a_line( block_start->line, &lens );
  969.    if (lens < bc || mode.inflate_tabs)
  970.       *rc = prepare_block( &s_w, block_start, bc );
  971.  
  972.    /*
  973.     * pad the end of the STREAM block if needed.
  974.     */
  975.    lens = block_end->len;
  976.    detab_a_line( block_end->line, &lens );
  977.    if (*rc == OK  &&  (lens < ec+1  ||  mode.inflate_tabs))
  978.       *rc = prepare_block( &s_w, block_end, ec+1 );
  979.  
  980.    /*
  981.     * pad the destination line if necessary
  982.     */
  983.    copy_line( dest );
  984.    detab_linebuff( );
  985.    *rc = un_copy_line( dest, &d_w, FALSE );
  986.    lend = dest->len;
  987.    if (*rc == OK && (action==MOVE || action==COPY || action==KOPY)) {
  988.       if (lend < rcol || mode.inflate_tabs)
  989.          *rc = prepare_block( &d_w, dest, rcol );
  990.    }
  991.  
  992.    if ((action == COPY || action == KOPY) && *rc == OK) {
  993.  
  994.       /*
  995.        * concatenate the end of the STREAM block with the end of the
  996.        *   destination line.
  997.        */
  998.       lens = dest->len - rcol;
  999.  
  1000.       assert( lens >= 0 );
  1001.       assert( lens <= MAX_LINE_LENGTH );
  1002.       assert( ec + 1 >= 0 );
  1003.       assert( ec + 1 <= MAX_LINE_LENGTH );
  1004.       assert( rcol >= 0 );
  1005.  
  1006.       _fmemcpy( g_status.line_buff, block_end->line, ec+1 );
  1007.       _fmemcpy( g_status.line_buff+ec+1, dest->line+rcol, lens );
  1008.       lens += ec + 1;
  1009.       g_status.line_buff_len = lens;
  1010.  
  1011.       temp_ll = (line_list_ptr)my_malloc( sizeof(line_list_struc), rc );
  1012.       if (*rc == OK) {
  1013.          temp_ll->line  = NULL;
  1014.          temp_ll->len   = 0;
  1015.          temp_ll->dirty = FALSE;
  1016.          g_status.copied = TRUE;
  1017.          *rc = un_copy_line( temp_ll, &d_w, TRUE );
  1018.  
  1019.          if (*rc == OK) {
  1020.             dest->next->prev = temp_ll;
  1021.             temp_ll->next = dest->next;
  1022.             dest->next = temp_ll;
  1023.             temp_ll->prev = dest;
  1024.          } else
  1025.             if (temp_ll != NULL)
  1026.                my_free( temp_ll );
  1027.       } else {
  1028.          if (temp_ll != NULL)
  1029.             my_free( temp_ll );
  1030.       }
  1031.  
  1032.       /*
  1033.        * file too big
  1034.        */
  1035.       if (*rc != OK)
  1036.          error( WARNING, window->bottom_line, dir3 );
  1037.  
  1038.       if (*rc == OK) {
  1039.          g_status.copied = FALSE;
  1040.          copy_line( dest );
  1041.          lens = block_start->len - bc;
  1042.  
  1043.          assert( lens >= 0 );
  1044.          assert( lens <= MAX_LINE_LENGTH );
  1045.          assert( bc >= 0 );
  1046.          assert( bc <= MAX_LINE_LENGTH );
  1047.          assert( rcol >= 0 );
  1048.  
  1049.          _fmemcpy( g_status.line_buff+rcol, block_start->line+bc, lens );
  1050.          lens = rcol + lens;
  1051.          g_status.line_buff_len = lens;
  1052.          *rc = un_copy_line( dest, &d_w, TRUE );
  1053.       }
  1054.  
  1055.       source = block_start->next;
  1056.       for (li=br+1; li < er  &&  *rc == OK; li++) {
  1057.          lens = source->len;
  1058.          temp_ll = (line_list_ptr)my_malloc( sizeof(line_list_struc), rc );
  1059.  
  1060.          assert( lens >= 0 );
  1061.          assert( lens <= MAX_LINE_LENGTH );
  1062.  
  1063.          l = (text_ptr)my_malloc( lens * sizeof(char), rc );
  1064.          if (*rc == OK) {
  1065.             if (lens > 0)
  1066.                _fmemcpy( l, source->line, lens );
  1067.             temp_ll->line  = l;
  1068.             temp_ll->len   = lens;
  1069.             temp_ll->dirty = TRUE;
  1070.  
  1071.             if (dest->next != NULL) {
  1072.                dest->next->prev = temp_ll;
  1073.                temp_ll->next = dest->next;
  1074.                dest->next = temp_ll;
  1075.                temp_ll->prev = dest;
  1076.             } else {
  1077.                temp_ll->next = dest;
  1078.                if (dest->prev != NULL)
  1079.                   dest->prev->next = temp_ll;
  1080.                temp_ll->prev = dest->prev;
  1081.                dest->prev = temp_ll;
  1082.                if (temp_ll->prev == NULL)
  1083.                   window->file_info->line_list = temp_ll;
  1084.             }
  1085.  
  1086.             dest = temp_ll;
  1087.             source = source->next;
  1088.          } else {
  1089.             /*
  1090.              * file too big
  1091.              */
  1092.             error( WARNING, window->bottom_line, dir3 );
  1093.             if (l != NULL)
  1094.                my_free( l );
  1095.             if (temp_ll != NULL)
  1096.                my_free( temp_ll );
  1097.             *rc = WARNING;
  1098.          }
  1099.       }
  1100.    } else if (action == MOVE) {
  1101.  
  1102.       /*
  1103.        * is the dest on the same line as the block_start?
  1104.        */
  1105.       if (ptoul( dest ) == ptoul( block_start )) {
  1106.  
  1107.          /*
  1108.           * move the text between rcol and bc in block_start->line
  1109.           *   to block_end->line + ec.
  1110.           */
  1111.          lens = bc - rcol;
  1112.          lend = block_end->len - (ec + 1);
  1113.          g_status.copied = FALSE;
  1114.          copy_line( block_end );
  1115.  
  1116.  
  1117.          assert( lens >= 0 );
  1118.          assert( lens <= MAX_LINE_LENGTH );
  1119.          assert( lend >= 0 );
  1120.          assert( lend <= MAX_LINE_LENGTH );
  1121.          assert( ec + lens + 1 <= MAX_LINE_LENGTH );
  1122.          assert( rcol >= 0 );
  1123.  
  1124.  
  1125.          _fmemmove( g_status.line_buff + ec + lens + 1,
  1126.                     g_status.line_buff + ec + 1,  lend );
  1127.          _fmemcpy( g_status.line_buff+ec+1, block_start->line+rcol, lens );
  1128.          g_status.line_buff_len = block_end->len + lens;
  1129.          *rc = un_copy_line( block_end, &d_w, TRUE );
  1130.  
  1131.          /*
  1132.           * now, remove the text between rcol and bc on block_start->line
  1133.           */
  1134.          if (*rc == OK) {
  1135.             lend = block_start->len - bc;
  1136.             copy_line( block_start );
  1137.  
  1138.             assert( lend >= 0 );
  1139.             assert( lend < MAX_LINE_LENGTH );
  1140.  
  1141.             _fmemmove( g_status.line_buff + rcol,
  1142.                        g_status.line_buff + bc, lend );
  1143.  
  1144.             assert( block_start->len - (bc - rcol) >= 0 );
  1145.             assert( block_start->len - (bc - rcol) <= MAX_LINE_LENGTH );
  1146.  
  1147.             g_status.line_buff_len = block_start->len - (bc - rcol);
  1148.             *rc = un_copy_line( block_start, &d_w, TRUE );
  1149.          }
  1150.  
  1151.       /*
  1152.        * is the dest on the same line as the block_end?
  1153.        */
  1154.       } else if (ptoul( dest ) == ptoul( block_end )) {
  1155.  
  1156.          /*
  1157.           * move the text between rcol and ec on block_end->line to
  1158.           *   block_start->line + bc.
  1159.           */
  1160.          lens = rcol - ec;
  1161.          lend = block_start->len - bc;
  1162.          g_status.copied = FALSE;
  1163.          copy_line( block_start );
  1164.  
  1165.          assert( lens >= 0 );
  1166.          assert( lens <= MAX_LINE_LENGTH );
  1167.          assert( lend >= 0 );
  1168.          assert( lend <= MAX_LINE_LENGTH );
  1169.          assert( bc + lens <= MAX_LINE_LENGTH );
  1170.          assert( ec + 1 >= 0 );
  1171.  
  1172.          _fmemmove( g_status.line_buff + bc + lens,
  1173.                     g_status.line_buff + bc,  lend );
  1174.          _fmemcpy( g_status.line_buff+bc, block_end->line+ec+1, lens );
  1175.  
  1176.          assert( block_start->len + lens >= 0 );
  1177.          assert( block_start->len + lens <= MAX_LINE_LENGTH );
  1178.  
  1179.          g_status.line_buff_len = block_start->len + lens;
  1180.          *rc = un_copy_line( block_start, &d_w, TRUE );
  1181.  
  1182.          /*
  1183.           * now, remove the text on block_end->line between rcol and ec
  1184.           */
  1185.          if (*rc == OK) {
  1186.             lend = block_end->len - (rcol + 1);
  1187.             copy_line( block_end );
  1188.  
  1189.             assert( lend >= 0 );
  1190.             assert( lend <= MAX_LINE_LENGTH );
  1191.             assert( ec + 1 >= 0 );
  1192.             assert( rcol + 1 >= 0 );
  1193.             assert( ec + 1 <= MAX_LINE_LENGTH );
  1194.             assert( rcol + 1 <= MAX_LINE_LENGTH );
  1195.             assert( block_end->len - (rcol - ec) >= 0 );
  1196.             assert( block_end->len - (rcol - ec) <= MAX_LINE_LENGTH );
  1197.  
  1198.  
  1199.             _fmemmove( g_status.line_buff + ec + 1,
  1200.                        g_status.line_buff + rcol + 1, lend );
  1201.             g_status.line_buff_len = block_end->len - (rcol - ec);
  1202.             *rc = un_copy_line( block_end, &d_w, TRUE );
  1203.          }
  1204.       } else {
  1205.  
  1206.          lens = dest->len - rcol;
  1207.  
  1208.          assert( ec + 1 >= 0 );
  1209.          assert( ec + 1 <= MAX_LINE_LENGTH );
  1210.          assert( lens >= 0 );
  1211.          assert( lens <= MAX_LINE_LENGTH );
  1212.          assert( rcol >= 0 );
  1213.          assert( rcol <= MAX_LINE_LENGTH );
  1214.  
  1215.          _fmemcpy( g_status.line_buff, block_end->line, ec+1 );
  1216.          _fmemcpy( g_status.line_buff+ec+1, dest->line+rcol, lens );
  1217.          lens += ec + 1;
  1218.          g_status.line_buff_len = lens;
  1219.  
  1220.          temp_ll = (line_list_ptr)my_malloc( sizeof(line_list_struc), rc );
  1221.          if (*rc == OK) {
  1222.             temp_ll->line  = NULL;
  1223.             temp_ll->len   = 0;
  1224.             temp_ll->dirty = FALSE;
  1225.             g_status.copied = TRUE;
  1226.             *rc = un_copy_line( temp_ll, &d_w, TRUE );
  1227.  
  1228.             if (*rc != ERROR) {
  1229.                dest->next->prev = temp_ll;
  1230.                temp_ll->next = dest->next;
  1231.                dest->next = temp_ll;
  1232.                temp_ll->prev = dest;
  1233.             } else
  1234.                if (temp_ll != NULL)
  1235.                   my_free( temp_ll );
  1236.          } else {
  1237.             if (temp_ll != NULL)
  1238.                my_free( temp_ll );
  1239.          }
  1240.  
  1241.          /*
  1242.           * file too big
  1243.           */
  1244.          if (*rc != OK)
  1245.             error( WARNING, window->bottom_line, dir3 );
  1246.  
  1247.          if (*rc == OK) {
  1248.             copy_line( dest );
  1249.             lens = block_start->len - bc;
  1250.  
  1251.             assert( bc >= 0 );
  1252.             assert( bc <= MAX_LINE_LENGTH );
  1253.             assert( lens >= 0 );
  1254.             assert( lens <= MAX_LINE_LENGTH );
  1255.             assert( rcol >= 0 );
  1256.             assert( rcol <= MAX_LINE_LENGTH );
  1257.  
  1258.             _fmemcpy( g_status.line_buff+rcol, block_start->line+bc, lens );
  1259.             g_status.line_buff_len = lens + rcol;
  1260.             *rc = un_copy_line( dest, &d_w, TRUE );
  1261.             dest->dirty = TRUE;
  1262.          }
  1263.  
  1264.          if (*rc == OK  &&  ptoul( block_start->next ) != ptoul( block_end )) {
  1265.             block_start->next->prev = dest;
  1266.             temp_ll->prev = block_end->prev;
  1267.             block_end->prev->next = temp_ll;
  1268.             dest->next = block_start->next;
  1269.          }
  1270.  
  1271.          if (*rc == OK) {
  1272.             copy_line( block_start );
  1273.             detab_linebuff( );
  1274.             lend = bc;
  1275.             lens = block_end->len - (ec + 1);
  1276.  
  1277.             assert( bc >= 0 );
  1278.             assert( bc <= MAX_LINE_LENGTH );
  1279.             assert( lens >= 0 );
  1280.             assert( lens <= MAX_LINE_LENGTH );
  1281.             assert( lend >= 0 );
  1282.             assert( lend <= MAX_LINE_LENGTH );
  1283.             assert( ec + 1 >= 0 );
  1284.             assert( ec + 1 <= MAX_LINE_LENGTH );
  1285.             assert( lens + lend >= 0 );
  1286.             assert( lens + lend <= MAX_LINE_LENGTH );
  1287.  
  1288.             _fmemcpy( g_status.line_buff+bc, block_end->line+ec+1, lens );
  1289.             g_status.line_buff_len = lend + lens;
  1290.             *rc = un_copy_line( block_start, &s_w, TRUE );
  1291.             block_start->dirty = TRUE;
  1292.             block_start->next = block_end->next;
  1293.             block_end->next->prev = block_start;
  1294.             if (block_end->line != NULL)
  1295.                my_free( block_end->line );
  1296.             my_free( block_end );
  1297.          }
  1298.       }
  1299.    } else if (action == DELETE) {
  1300.       copy_line( block_start );
  1301.       lens = block_end->len - (ec + 1);
  1302.  
  1303.       assert( bc >= 0 );
  1304.       assert( bc <= MAX_LINE_LENGTH );
  1305.       assert( lens >= 0 );
  1306.       assert( lens <= MAX_LINE_LENGTH );
  1307.       assert( ec + 1 >= 0 );
  1308.       assert( ec + 1 <= MAX_LINE_LENGTH );
  1309.       assert( bc + lens >= 0 );
  1310.       assert( bc + lens <= MAX_LINE_LENGTH );
  1311.  
  1312.       _fmemcpy( g_status.line_buff+bc, block_end->line + ec+1, lens );
  1313.       g_status.line_buff_len = bc + lens;
  1314.       *rc = un_copy_line( block_start, &s_w, TRUE );
  1315.       block_start->dirty = TRUE;
  1316.       source = block_start->next;
  1317.       block_start->next = block_end->next;
  1318.       block_end->next->prev = block_start;
  1319.       block_end->next = NULL;
  1320.       while (source != NULL) {
  1321.          temp_ll = source;
  1322.          source = source->next;
  1323.          if (temp_ll->line != NULL)
  1324.             my_free( temp_ll->line );
  1325.          my_free( temp_ll );
  1326.       }
  1327.    }
  1328.  
  1329.    if (*rc == OK) {
  1330.       diff = er - br;
  1331.       if (action == COPY || action == KOPY || action == MOVE)
  1332.          dest_file->length += diff;
  1333.       if (action == DELETE || action == MOVE)
  1334.          source_file->length -= diff;
  1335.       if (action == DELETE && source_window->rline >= br) {
  1336.          source_window->rline -= diff;
  1337.          if (source_window->rline < br)
  1338.             source_window->rline = br;
  1339.       }
  1340.    }
  1341.  
  1342.    /*
  1343.     * restore all cursors in all windows
  1344.     */
  1345.    restore_cursors( dest_file );
  1346.    if (dest_file != source_file)
  1347.       restore_cursors( source_file );
  1348.    show_avail_mem( );
  1349. }
  1350.  
  1351.  
  1352. /*
  1353.  * Name:    do_box_block
  1354.  * Purpose: delete, move, copy, or kopy a BOX block
  1355.  * Date:    June 5, 1991
  1356.  * Passed:  window:         pointer to destination window (current window)
  1357.  *          source_window:  pointer to source window
  1358.  *          action:         block action  --  OVERLAY, FILL, etc...
  1359.  *          source_file:    pointer to source file structure
  1360.  *          dest_file:      pointer to destination file
  1361.  *          source:         pointer to source node
  1362.  *          dest:           pointer to destination node
  1363.  *          br:             beginning line number in marked block
  1364.  *          er:             ending line number in marked block
  1365.  *          block_inc:      increment used to number a block
  1366.  *          rline:          current line number in destination file
  1367.  *          block_num:      starting number when numbering a block
  1368.  *          block_just:     LEFT or RIGHT justified numbers in block
  1369.  *          fill_char:      character to fill a block
  1370.  *          same:           are source and destination files same? T or F
  1371.  *          block_len:      width of box block
  1372.  *          bc:             beginning column of block
  1373.  *          ec:             ending column of block
  1374.  *          rcol:           current column of cursor
  1375.  *          rc:             return code
  1376.  */
  1377. void do_box_block( WINDOW *window,  WINDOW *source_window,  int action,
  1378.                     file_infos *source_file,  file_infos *dest_file,
  1379.                     line_list_ptr source,  line_list_ptr dest, long br,
  1380.                     long er, long block_inc,
  1381.                     long rline, long block_num, int block_just, int fill_char,
  1382.                     int same, int block_len, int bc, int ec, int rcol, int *rc )
  1383. {
  1384. line_list_ptr p;                /* temporary list pointer */
  1385. int  lens;                      /* length of source line */
  1386. int  lend;                      /* length of destination line */
  1387. int  add;                       /* characters being added from another line */
  1388. char *block_buff;
  1389. char *swap_buff;
  1390. int  xbc, xec;                  /* temporary column variables */
  1391. long li;                        /* temporary line variables */
  1392. long dest_add;                  /* number of bytes added to destination file */
  1393. WINDOW s_w, d_w;       /* a couple of temporary WINDOWs for BOX stuff */
  1394. int  padded_file;
  1395. WINDOW *w;
  1396.  
  1397.    padded_file = FALSE;
  1398.    dup_window_info( &s_w, source_window );
  1399.    dup_window_info( &d_w, window );
  1400.    s_w.rline   = br;
  1401.    s_w.ll      = source;
  1402.    s_w.visible = FALSE;
  1403.    d_w.rline   = rline;
  1404.    d_w.ll      = dest;
  1405.    d_w.visible = FALSE;
  1406.  
  1407.    block_buff = (char *)calloc( BUFF_SIZE + 2, sizeof(char) );
  1408.    swap_buff  = (char *)calloc( BUFF_SIZE + 2, sizeof(char) );
  1409.    if (block_buff == NULL || swap_buff == NULL) {
  1410.       error( WARNING, window->bottom_line, block4 );
  1411.       *rc = ERROR;
  1412.    }
  1413.  
  1414.    /*
  1415.     * special case for block actions.  since block actions always
  1416.     *   move forward thru the file, overlapping text in an OVERLAY
  1417.     *   action don't do right.  make the operation start at the end
  1418.     *   of the block and work backwards.
  1419.     */
  1420.    if (*rc == OK  &&  (action == OVERLAY || action == SWAP) &&
  1421.            same  &&  rline > br  &&  rline <= er) {
  1422.  
  1423.       /*
  1424.        * see if we need to add padd lines at eof.
  1425.        */
  1426.       dest_add = rline - br;
  1427.       if (dest_add + er > window->file_info->length) {
  1428.          dest_add = dest_add - (window->file_info->length - er);
  1429.          p = dest_file->line_list_end->prev;
  1430.          for (; dest_add > 0  &&  *rc == OK; dest_add--)
  1431.             *rc = pad_dest_line( window, dest_file, p );
  1432.          padded_file = TRUE;
  1433.       }
  1434.  
  1435.       /*
  1436.        * move source and dest pointers to the end of the OVERLAY
  1437.        */
  1438.       for (li=er-br; li > 0; li--) {
  1439.          load_undo_buffer( dest_file, dest->line, dest->len );
  1440.          dest = dest->next;
  1441.          ++d_w.rline;
  1442.          source = source->next;
  1443.          ++s_w.rline;
  1444.       }
  1445.  
  1446.       /*
  1447.        * work backwards so the overlapped OVERLAY block don't use
  1448.        * overlayed text to fill the block.  same for SWAPPing blocks.
  1449.        */
  1450.       for (li=er; *rc == OK  &&  li >= br  &&  !g_status.control_break;
  1451.                                        li--, s_w.rline--, d_w.rline--) {
  1452.          lens = find_end( source->line, source->len );
  1453.          lend = find_end( dest->line, dest->len );
  1454.          if (lens != 0 || lend != 0) {
  1455.             load_box_buff( block_buff, source, bc, ec, ' ' );
  1456.             if (action == SWAP)
  1457.                load_box_buff( swap_buff, dest, rcol, rcol+block_len, ' ' );
  1458.             *rc = copy_buff_2file( &d_w, block_buff, dest, rcol,
  1459.                                     block_len, action );
  1460.             dest->dirty = TRUE;
  1461.             if (action == SWAP) {
  1462.                add = 0;
  1463.                *rc = copy_buff_2file( &s_w, swap_buff, source, bc,
  1464.                                 block_len, action );
  1465.                source->dirty = TRUE;
  1466.             }
  1467.          }
  1468.          source = source->prev;
  1469.          dest = dest->prev;
  1470.       }
  1471.    } else {
  1472.       if (action == FILL)
  1473.          block_fill( block_buff, fill_char, block_len );
  1474.       for (li=br; *rc == OK  &&  li <= er  &&  !g_status.control_break;
  1475.                            li++, s_w.rline++, d_w.rline++) {
  1476.          lens = find_end( source->line, source->len );
  1477.          lend = find_end( dest->line, dest->len );
  1478.  
  1479.          switch (action) {
  1480.             case FILL    :
  1481.             case NUMBER  :
  1482.             case DELETE  :
  1483.             case MOVE    :
  1484.                load_undo_buffer( source_file, source->line, source->len );
  1485.                break;
  1486.             case COPY    :
  1487.             case KOPY    :
  1488.             case OVERLAY :
  1489.                load_undo_buffer( dest_file, dest->line, dest->len );
  1490.                break;
  1491.          }
  1492.  
  1493.          /*
  1494.           * with FILL and NUMBER operations, we're just adding chars
  1495.           *   to the file at the source location.  we don't have to
  1496.           *   worry about bookkeeping.
  1497.           */
  1498.          if (action == FILL || action == NUMBER) {
  1499.             if (action == NUMBER) {
  1500.               number_block_buff( block_buff, block_len, block_num, block_just );
  1501.               block_num += block_inc;
  1502.             }
  1503.             *rc = copy_buff_2file( &s_w, block_buff, source, rcol,
  1504.                                 block_len, action );
  1505.             source->dirty = TRUE;
  1506.  
  1507.          /*
  1508.           * if we are doing a BOX action and both the source and
  1509.           * destination are 0 then we have nothing to do.
  1510.           */
  1511.          } else if (lens != 0 || lend != 0) {
  1512.  
  1513.             /*
  1514.              * do actions that may require adding to file
  1515.              */
  1516.             if (action == MOVE     ||  action == COPY || action == KOPY ||
  1517.                 action == OVERLAY  ||  action == SWAP) {
  1518.                xbc = bc;
  1519.                xec = ec;
  1520.                if (action != OVERLAY  &&  action != SWAP  &&  same) {
  1521.                   if (rcol < bc && rline > br && rline <=er)
  1522.                      if (li >= rline) {
  1523.                         xbc = bc + block_len;
  1524.                         xec = ec + block_len;
  1525.                      }
  1526.                }
  1527.                load_box_buff( block_buff, source, xbc, xec, ' ' );
  1528.                if (action == SWAP)
  1529.                   load_box_buff( swap_buff, dest, rcol, rcol+block_len, ' ' );
  1530.                *rc = copy_buff_2file( &d_w, block_buff, dest, rcol,
  1531.                                 block_len, action );
  1532.                dest->dirty = TRUE;
  1533.                if (action == SWAP && *rc == OK) {
  1534.                   *rc = copy_buff_2file( &s_w, swap_buff, source, xbc,
  1535.                                    block_len, action );
  1536.                   source->dirty = TRUE;
  1537.                }
  1538.             }
  1539.  
  1540.             /*
  1541.              * do actions that may require deleting from file
  1542.              */
  1543.             if (action == MOVE || action == DELETE) {
  1544.                lens = find_end( source->line, source->len );
  1545.                if (lens >= (bc + 1)) {
  1546.                   source->dirty = TRUE;
  1547.                   add = block_len;
  1548.                   xbc = bc;
  1549.                   if (lens <= (ec + 1))
  1550.                      add = lens - bc;
  1551.                   if (same && action == MOVE) {
  1552.                      if (rcol < bc && rline >= br && rline <=er)
  1553.                         if (li >= rline) {
  1554.                            xbc = bc + block_len;
  1555.                            if (lens <= (ec + block_len + 1))
  1556.                               add = lens - xbc;
  1557.                         }
  1558.                   }
  1559.                   if (add > 0)
  1560.                      *rc = delete_box_block( &s_w, source, xbc, add );
  1561.                }
  1562.             }
  1563.          }
  1564.  
  1565.          /*
  1566.           * if we are doing any BOX action we need to move the source pointer
  1567.           * to the next line.
  1568.           */
  1569.          source = source->next;
  1570.  
  1571.          /*
  1572.           * if we are doing any action other than DELETE, we need to move
  1573.           * the destination to the next line in marked block.
  1574.           * In BOX mode, we may need to pad the end of the file
  1575.           * with a blank line before we process the next line.
  1576.           */
  1577.          if (action != DELETE && action != FILL && action != NUMBER) {
  1578.             p = dest->next;
  1579.             if (p->len != EOF)
  1580.                dest = p;
  1581.             else if (li < er) {
  1582.                padded_file = TRUE;
  1583.                pad_dest_line( window, dest_file, p );
  1584.                dest = dest->next;
  1585.             }
  1586.          }
  1587.       }
  1588.    }
  1589.    if (block_buff != NULL)
  1590.       free( block_buff );
  1591.    if (swap_buff != NULL)
  1592.       free( swap_buff );
  1593.    if (padded_file) {
  1594.       w = g_status.window_list;
  1595.       while (w != NULL) {
  1596.          if (w->file_info == dest_file  &&  w->visible)
  1597.             show_size( w );
  1598.          w = w->next;
  1599.       }
  1600.    }
  1601.    show_avail_mem( );
  1602. }
  1603.  
  1604.  
  1605. /*
  1606.  * Name:    load_box_buff
  1607.  * Class:   helper function
  1608.  * Purpose: copy the contents of a BOX to a block buffer.
  1609.  * Date:    June 5, 1991
  1610.  * Passed:  block_buff: local buffer for block moves
  1611.  *          ll:         node to source line in file to load
  1612.  *          bc:     beginning column of BOX. used only in BOX operations.
  1613.  *          ec:     ending column of BOX. used only in BOX operations.
  1614.  *          filler: character to fill boxes that end past eol
  1615.  * Notes:   For BOX blocks, there are several things to take care of:
  1616.  *            1) The BOX begins and ends within a line - just copy the blocked
  1617.  *            characters to the block buff.  2) the BOX begins within a line
  1618.  *            but ends past the eol - copy all the characters within the line
  1619.  *            to the block buff then fill with padding.  3) the BOX begins and
  1620.  *            ends past eol - fill entire block buff with padding (filler).
  1621.  *          the fill character varies with the block operation.  for sorting
  1622.  *            a box block, the fill character is '\0'.  for adding text to
  1623.  *            the file, the fill character is a space.
  1624.  */
  1625. void load_box_buff( char *block_buff, line_list_ptr ll, int bc, int ec,
  1626.                     char filler )
  1627. {
  1628. int len;
  1629. int avlen;
  1630. register int i;
  1631. register char *bb;
  1632. text_ptr s;
  1633.  
  1634.    assert( bc >= 0 );
  1635.    assert( ec >= bc );
  1636.    assert( ec < MAX_LINE_LENGTH );
  1637.  
  1638.    bb = block_buff;
  1639.    len = ll->len;
  1640.    s = detab_a_line( ll->line, &len );
  1641.    /*
  1642.     * block start may be past eol
  1643.     */
  1644.    if (len < ec + 1) {
  1645.       /*
  1646.        * does block start past eol? - fill with pad
  1647.        */
  1648.       assert( ec + 1 - bc >= 0 );
  1649.  
  1650.       memset( block_buff, filler, (ec + 1) - bc );
  1651.       if (len >= bc) {
  1652.          /*
  1653.           * block ends past eol - fill with pad
  1654.           */
  1655.          avlen = len - bc;
  1656.          s += bc;
  1657.          for (i=avlen; i>0; i--)
  1658.             *bb++ = *s++;
  1659.       }
  1660.    } else {
  1661.       /*
  1662.        * block is within line - copy block to buffer
  1663.        */
  1664.       avlen = (ec + 1) - bc;
  1665.       s = s + bc;
  1666.       for (i=avlen; i>0; i--)
  1667.          *bb++ = *s++;
  1668.    }
  1669. }
  1670.  
  1671.  
  1672. /*
  1673.  * Name:    copy_buff_2file
  1674.  * Class:   helper function
  1675.  * Purpose: copy the contents of block buffer to destination file
  1676.  * Date:    June 5, 1991
  1677.  * Passed:  window:     pointer to current window
  1678.  *          block_buff: local buffer for moves
  1679.  *          dest:       pointer to destination line in destination file
  1680.  *          rcol:       if in BOX mode, destination column in destination file
  1681.  *          block_len:  if in BOX mode, width of block to copy
  1682.  *          action:     type of block action
  1683.  * Notes:   In BOX mode, the destination line has already been prepared.
  1684.  *          Just copy the BOX buffer to the destination line.
  1685.  */
  1686. int  copy_buff_2file( WINDOW *window, char *block_buff, line_list_ptr dest,
  1687.                       int rcol, int block_len, int action )
  1688. {
  1689. char *s;
  1690. char *d;
  1691. int len;
  1692. int pad;
  1693. int add;
  1694.  
  1695.    copy_line( dest );
  1696.    if (mode.inflate_tabs)
  1697.       detab_linebuff( );
  1698.  
  1699.    len = g_status.line_buff_len;
  1700.  
  1701.    assert( len >= 0 );
  1702.    assert( len < MAX_LINE_LENGTH );
  1703.    assert( rcol >= 0 );
  1704.    assert( rcol < MAX_LINE_LENGTH );
  1705.    assert( block_len >= 0 );
  1706.    assert( block_len < BUFF_SIZE );
  1707.  
  1708.    if (rcol > len) {
  1709.       pad = rcol - len;
  1710.  
  1711.       assert( pad >= 0 );
  1712.       assert( pad < MAX_LINE_LENGTH );
  1713.  
  1714.       memset( g_status.line_buff + len, ' ', pad );
  1715.       len += pad;
  1716.    }
  1717.  
  1718.    s = g_status.line_buff + rcol;
  1719.  
  1720.    /*
  1721.     * s is pointing to location to perform BOX operation.  If we do a
  1722.     * FILL or OVERLAY, we do not necessarily add any extra space.  If the
  1723.     * line does not extend all the thru the BOX then we add.
  1724.     * we always add space when we COPY, KOPY, or MOVE
  1725.     */
  1726.    if (action == FILL || action == OVERLAY || action == NUMBER || action == SWAP) {
  1727.       add = len - rcol;
  1728.       if (add < block_len) {
  1729.          pad = block_len - add;
  1730.  
  1731.          assert( pad >= 0 );
  1732.          assert( pad < MAX_LINE_LENGTH );
  1733.  
  1734.          memset( g_status.line_buff + len, ' ', pad );
  1735.          len += pad;
  1736.       }
  1737.    } else {
  1738.       d = s + block_len;
  1739.       add = len - rcol;
  1740.  
  1741.       assert( add >= 0 );
  1742.       assert( add < MAX_LINE_LENGTH );
  1743.  
  1744.       memmove( d, s, add );
  1745.       len += block_len;
  1746.    }
  1747.  
  1748.    assert( rcol + block_len <= len );
  1749.    assert( len >= 0 );
  1750.    assert( len < MAX_LINE_LENGTH );
  1751.  
  1752.    memmove( s, block_buff, block_len );
  1753.    g_status.line_buff_len = len;
  1754.    if (mode.inflate_tabs)
  1755.       entab_linebuff( );
  1756.    return( un_copy_line( dest, window, TRUE ) );
  1757. }
  1758.  
  1759.  
  1760. /*
  1761.  * Name:    block_fill
  1762.  * Class:   helper function
  1763.  * Purpose: fill the block buffer with character
  1764.  * Date:    June 5, 1991
  1765.  * Passed:  block_buff: local buffer for moves
  1766.  *          fill_char:  fill character
  1767.  *          block_len:  number of columns in block
  1768.  * Notes:   Fill block_buffer for block_len characters using fill_char.  This
  1769.  *          function is used only for BOX blocks.
  1770.  */
  1771. void block_fill( char *block_buff, int fill_char, int block_len )
  1772. {
  1773.    assert( block_len >= 0 );
  1774.    assert( block_len < BUFF_SIZE );
  1775.    assert( block_buff != NULL );
  1776.  
  1777.    memset( block_buff, fill_char, block_len );
  1778. }
  1779.  
  1780.  
  1781. /*
  1782.  * Name:    number_block_buff
  1783.  * Class:   helper function
  1784.  * Purpose: put a number into the block buffer
  1785.  * Date:    June 5, 1991
  1786.  * Passed:  block_buff: local buffer for moves
  1787.  *          block_len:  number of columns in block
  1788.  *          block_num:  long number to fill block
  1789.  *          just:       LEFT or RIGHT justified?
  1790.  * Notes:   Fill block_buffer for block_len characters with number.
  1791.  *          This function is used only for BOX blocks.
  1792.  */
  1793. void number_block_buff( char *block_buff, int block_len, long block_num,
  1794.                         int just )
  1795. {
  1796. int len;                /* length of number buffer */
  1797. int i;
  1798. char temp[MAX_COLS];    /* buffer for long number to ascii conversion  */
  1799.  
  1800.    assert( block_len >= 0 );
  1801.    assert( block_len < BUFF_SIZE );
  1802.  
  1803.    block_fill( block_buff, ' ', block_len );
  1804.    len = strlen( ltoa( block_num, temp, 10 ) );
  1805.    if (just == RIGHT) {
  1806.       block_len--;
  1807.       len--;
  1808.       for (;block_len >= 0 && len >= 0; block_len--, len--)
  1809.          block_buff[block_len] = temp[len];
  1810.    } else {
  1811.       for (i=0; block_len > 0 && i < len; block_len--, i++)
  1812.          block_buff[i] = temp[i];
  1813.    }
  1814. }
  1815.  
  1816.  
  1817. /*
  1818.  * Name:    restore_cursors
  1819.  * Class:   helper function
  1820.  * Purpose: a file has been modified - must restore all cursor pointers
  1821.  * Date:    June 5, 1991
  1822.  * Passed:  file:  pointer to file with changes
  1823.  * Notes:   Go through the window list and adjust the cursor pointers
  1824.  *          as needed.
  1825.  */
  1826. void restore_cursors( file_infos *file )
  1827. {
  1828. register WINDOW *window;
  1829. line_list_ptr ll;
  1830. long n;
  1831.  
  1832.    assert( file != NULL );
  1833.  
  1834.    window = g_status.window_list;
  1835.    while (window != NULL) {
  1836.       if (window->file_info == file) {
  1837.          window->bin_offset = 0;
  1838.          if (window->rline < 1L)
  1839.             window->rline = 1L;
  1840.          if (window->rline > file->length)
  1841.             window->rline = file->length;
  1842.          ll = file->line_list;
  1843.          n = 1L;
  1844.          for (; n < window->rline; n++) {
  1845.             window->bin_offset += ll->len;
  1846.             ll = ll->next;
  1847.          }
  1848.          window->ll = ll;
  1849.          if (window->rline < (window->cline - (window->top_line+window->ruler-1)))
  1850.             window->cline = (int)window->rline + window->top_line+window->ruler-1;
  1851.          if (window->cline < window->top_line + window->ruler)
  1852.             window->cline = window->top_line + window->ruler;
  1853.          if (window->visible)
  1854.             show_size( window );
  1855.       }
  1856.       window = window->next;
  1857.    }
  1858. }
  1859.  
  1860.  
  1861. /*
  1862.  * Name:    delete_box_block
  1863.  * Class:   helper function
  1864.  * Purpose: delete the marked text
  1865.  * Date:    June 5, 1991
  1866.  * Passed:  s_w:    source window
  1867.  *          source: pointer to line with block to delete
  1868.  *          bc:     beginning column of block - BOX mode only
  1869.  *          add:    number of characters in block to delete
  1870.  * Notes:   Used only for BOX blocks.  Delete the block.
  1871.  */
  1872. int  delete_box_block( WINDOW *s_w, line_list_ptr source, int bc, int add )
  1873. {
  1874. char *s;
  1875. int number;
  1876.  
  1877.    assert( s_w != NULL );
  1878.    assert( source != NULL );
  1879.    assert( bc >= 0 );
  1880.    assert( bc < MAX_LINE_LENGTH );
  1881.    assert( add >= 0 );
  1882.    assert( add < MAX_LINE_LENGTH );
  1883.  
  1884.    copy_line( source );
  1885.    detab_linebuff( );
  1886.    number = g_status.line_buff_len - bc;
  1887.    s = g_status.line_buff + bc + add;
  1888.  
  1889.    assert( number >= 0 );
  1890.    assert( number < MAX_LINE_LENGTH );
  1891.    assert( bc + add >= 0 );
  1892.    assert( bc + add < MAX_LINE_LENGTH );
  1893.    assert( add <= g_status.line_buff_len );
  1894.  
  1895.    memmove( s - add, s, number );
  1896.    g_status.line_buff_len -= add;
  1897.    entab_linebuff( );
  1898.    return( un_copy_line( source, s_w, TRUE ) );
  1899. }
  1900.  
  1901.  
  1902. /*
  1903.  * Name:    check_block
  1904.  * Class:   helper function
  1905.  * Purpose: To check that the block is still valid.
  1906.  * Date:    June 5, 1991
  1907.  * Notes:   After some editing, the marked block may not be valid.  For example,
  1908.  *          deleting all the lines in a block in another window.  We don't
  1909.  *          need to keep up with the block text pointers while doing normal
  1910.  *          editing; however, we need to refresh them before doing block stuff.
  1911.  */
  1912. void check_block( void )
  1913. {
  1914. register file_infos *file;
  1915. WINDOW filler;
  1916.  
  1917.    file = g_status.marked_file;
  1918.    if (file == NULL || file->block_br > file->length)
  1919.       unmark_block( &filler );
  1920.    else {
  1921.       if (file->block_er > file->length)
  1922.          file->block_er = file->length;
  1923.       find_begblock( file );
  1924.       find_endblock( file );
  1925.    }
  1926. }
  1927.  
  1928.  
  1929. /*
  1930.  * Name:    find_begblock
  1931.  * Class:   helper editor function
  1932.  * Purpose: find the beginning line in file with marked block
  1933.  * Date:    June 5, 1991
  1934.  * Passed:  file: file containing marked block
  1935.  * Notes:   file->block_start contains starting line of marked block.
  1936.  */
  1937. void find_begblock( file_infos *file )
  1938. {
  1939. line_list_ptr ll;
  1940. long li;           /* line counter (long i) */
  1941.  
  1942.    assert( file != NULL );
  1943.    assert( file->line_list != NULL );
  1944.  
  1945.    ll = file->line_list;
  1946.    for (li=1; li<file->block_br && ll->next != NULL; li++)
  1947.       ll = ll->next;
  1948.  
  1949.    file->block_start = ll;
  1950. }
  1951.  
  1952.  
  1953. /*
  1954.  * Name:    find_endblock
  1955.  * Class:   helper function
  1956.  * Purpose: find the ending line in file with marked block
  1957.  * Date:    June 5, 1991
  1958.  * Passed:  file: file containing marked block
  1959.  * Notes:   If in LINE mode, file->block_end is set to end of line of last
  1960.  *          line in block.  If in BOX mode, file->block_end is set to
  1961.  *          beginning of last line in marked block.  If the search for the
  1962.  *          ending line of the marked block goes past the eof, set the
  1963.  *          ending line of the block to the last line in the file.
  1964.  */
  1965. void find_endblock( file_infos *file )
  1966. {
  1967. line_list_ptr ll; /* start from beginning of file and go to end */
  1968. long i;           /* line counter */
  1969. register file_infos *fp;
  1970.  
  1971.    assert( file != NULL );
  1972.    assert( file->block_start != NULL );
  1973.  
  1974.    fp = file;
  1975.    ll = fp->block_start;
  1976.    if (ll != NULL) {
  1977.       for (i=fp->block_br;  i < fp->block_er && ll->next != NULL; i++)
  1978.          ll = ll->next;
  1979.       if (ll != NULL)
  1980.          fp->block_end = ll;
  1981.       else {
  1982.  
  1983.          /*
  1984.           * last line in marked block is NULL.  if LINE block, set end to
  1985.           * last character in the file.  if STREAM or BOX block, set end to
  1986.           * start of last line in file.  ending row, or er, is then set to
  1987.           * file length.
  1988.           */
  1989.          fp->block_end = fp->line_list_end->prev;
  1990.          fp->block_er = fp->length;
  1991.       }
  1992.    }
  1993. }
  1994.  
  1995.  
  1996. /*
  1997.  * Name:    block_write
  1998.  * Class:   primary editor function
  1999.  * Purpose: To write the currently marked block to a disk file.
  2000.  * Date:    June 5, 1991
  2001.  * Passed:  window:  pointer to current window
  2002.  * Notes:   If the file already exists, the user gets to choose whether
  2003.  *           to overwrite or append.
  2004.  */
  2005. int  block_write( WINDOW *window )
  2006. {
  2007. int prompt_line;
  2008. int rc;
  2009. char buff[MAX_COLS+2]; /* buffer for char and attribute  */
  2010. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  2011. file_infos *file;
  2012. int block_type;
  2013. int fattr;
  2014.  
  2015.    /*
  2016.     * make sure block is marked OK
  2017.     */
  2018.    entab_linebuff( );
  2019.    rc = un_copy_line( window->ll, window, TRUE );
  2020.    check_block( );
  2021.    if (rc == OK  &&  g_status.marked == TRUE) {
  2022.       prompt_line = window->bottom_line;
  2023.       file        = g_status.marked_file;
  2024.  
  2025.       assert( file != NULL );
  2026.  
  2027.       block_type  = file->block_type;
  2028.  
  2029.       /*
  2030.        * find out which file to write to
  2031.        */
  2032.       save_screen_line( 0, prompt_line, line_buff );
  2033.       *g_status.rw_name = '\0';
  2034.       if (get_name( block6, prompt_line, g_status.rw_name,
  2035.                     g_display.message_color ) == OK) {
  2036.          /*
  2037.           * if the file exists, find out whether to overwrite or append
  2038.           */
  2039.          rc = get_fattr( g_status.rw_name, &fattr );
  2040.          if (rc == OK) {
  2041.             /*
  2042.              * file exists. overwrite or append?
  2043.              */
  2044.             set_prompt( block7, prompt_line );
  2045.             switch (get_oa( )) {
  2046.                case A_OVERWRITE :
  2047.                   change_mode( g_status.rw_name, prompt_line );
  2048.                   /*
  2049.                    * writing block to
  2050.                    */
  2051.                   combine_strings( buff, block8, g_status.rw_name, "'" );
  2052.                   s_output( buff, prompt_line, 0, g_display.message_color );
  2053.                   rc = hw_save( g_status.rw_name, file, file->block_br,
  2054.                                 file->block_er, block_type );
  2055.                   if (rc == ERROR)
  2056.                      /*
  2057.                       * could not write block
  2058.                       */
  2059.                      error( WARNING, prompt_line, block9 );
  2060.                   break;
  2061.                case A_APPEND :
  2062.                   /*
  2063.                    * appending block to
  2064.                    */
  2065.                   combine_strings( buff, block10, g_status.rw_name, "'" );
  2066.                   s_output( buff, prompt_line, 0, g_display.message_color );
  2067.                   rc = hw_append( g_status.rw_name, file, file->block_br,
  2068.                                   file->block_er, block_type );
  2069.                   if (rc == ERROR)
  2070.                      /*
  2071.                       * could not append block
  2072.                       */
  2073.                      error( WARNING, prompt_line, block11 );
  2074.                   break;
  2075.                case AbortCommand :
  2076.                default :
  2077.                   rc = ERROR;
  2078.                   break;
  2079.             }
  2080.          } else if (rc != ERROR) {
  2081.             /*
  2082.              * writing block to
  2083.              */
  2084.             combine_strings( buff, block12, g_status.rw_name, "'" );
  2085.             s_output( buff, prompt_line, 0, g_display.message_color );
  2086.             if (hw_save( g_status.rw_name, file, file->block_br, file->block_er,
  2087.                          block_type ) == ERROR) {
  2088.                /*
  2089.                 * could not write block
  2090.                 */
  2091.                error( WARNING, prompt_line, block9 );
  2092.                rc = ERROR;
  2093.             }
  2094.          }
  2095.       }
  2096.       restore_screen_line( 0, prompt_line, line_buff );
  2097.    } else
  2098.       rc = ERROR;
  2099.    return( rc );
  2100. }
  2101.  
  2102.  
  2103. /*
  2104.  * Name:    block_print
  2105.  * Class:   primary editor function
  2106.  * Purpose: Print an entire file or the currently marked block.
  2107.  * Date:    June 5, 1991
  2108.  * Passed:  window:  pointer to current window
  2109.  * Notes:   With the added Critical Error Handler routine, let's fflush
  2110.  *          the print buffer first.
  2111.  */
  2112. int  block_print( WINDOW *window )
  2113. {
  2114. char answer[MAX_COLS];          /* entire file or just marked block? */
  2115. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  2116. int  col;
  2117. int  func;
  2118. int  prompt_line;
  2119. line_list_ptr block_start;   /* start of block in file */
  2120. file_infos *file;
  2121. int  block_type;
  2122. char *p;
  2123. int  len;
  2124. int  bc;
  2125. int  ec;
  2126. int  last_c;
  2127. long lbegin;
  2128. long lend;
  2129. long l;
  2130. int  color;
  2131. int  rc;
  2132.  
  2133.    color = g_display.message_color;
  2134.    entab_linebuff( );
  2135.    if (un_copy_line( window->ll, window, TRUE ) == ERROR)
  2136.       return( ERROR );
  2137.    rc = OK;
  2138.    prompt_line = window->bottom_line;
  2139.    save_screen_line( 0, prompt_line, line_buff );
  2140.    /*
  2141.     * print entire file or just marked block?
  2142.     */
  2143.  
  2144.    assert( strlen( block13 ) < MAX_COLS );
  2145.  
  2146.    strcpy( answer, block13 );
  2147.    col = strlen( answer );
  2148.    s_output( answer, prompt_line, 0, color );
  2149.    eol_clear( col, prompt_line, g_display.text_color );
  2150.    xygoto( col, prompt_line );
  2151.    func = col = 0;
  2152.    while (col != 'f' && col != 'F' && col != 'b' && col != 'B' &&
  2153.           func != AbortCommand) {
  2154.       col = getkey( );
  2155.       func = getfunc( col );
  2156.       if (col == ESC  ||  func == AbortCommand)
  2157.          rc = ERROR;
  2158.    }
  2159.  
  2160.    if (rc == OK) {
  2161.       /*
  2162.        * if everything is everything, flush the printer before we start
  2163.        *   printing.  then, check the critical error flag after the flush.
  2164.        */
  2165.       fflush( stdprn );
  2166.       if (ceh.flag == ERROR)
  2167.          rc = ERROR;
  2168.    }
  2169.  
  2170.    if (rc != ERROR) {
  2171.       file = window->file_info;
  2172.       block_type  = NOTMARKED;
  2173.       if (col == 'f' || col == 'F') {
  2174.          block_start = file->line_list;
  2175.          lend =   l  = file->length;
  2176.       } else {
  2177.          check_block( );
  2178.          if (g_status.marked == TRUE) {
  2179.             file        = g_status.marked_file;
  2180.             block_start = file->block_start;
  2181.             block_type  = file->block_type;
  2182.             lend =   l  = file->block_er + 1l - file->block_br;
  2183.          } else
  2184.             rc = ERROR;
  2185.       }
  2186.  
  2187.       if (rc != ERROR) {
  2188.          eol_clear( 0, prompt_line, color );
  2189.          /*
  2190.           * printing line   of    press control-break to cancel.
  2191.           */
  2192.          s_output( block14, prompt_line, 0, color );
  2193.          ltoa( l, answer, 10 );
  2194.          s_output( answer, prompt_line, 25, color );
  2195.          xygoto( 14, prompt_line );
  2196.          if (block_type == BOX || block_type == STREAM) {
  2197.             bc = file->block_bc;
  2198.             ec = file->block_ec;
  2199.             last_c = ec + 1 - bc;
  2200.          }
  2201.          p = g_status.line_buff;
  2202.          lbegin = 1;
  2203.          for (col=OK; l>0 && col == OK && !g_status.control_break; l--) {
  2204.             ltoa( lbegin, answer, 10 );
  2205.             s_output( answer, prompt_line, 14, color );
  2206.             g_status.copied = FALSE;
  2207.             if (block_type == BOX) {
  2208.                load_box_buff( p, block_start, bc, ec, ' ' );
  2209.                len = last_c;
  2210.             } else if (block_type == STREAM && lbegin == 1) {
  2211.                len = block_start->len;
  2212.                detab_a_line( block_start->line, &len );
  2213.                if (bc > len)
  2214.                   len = 0;
  2215.                else {
  2216.                   if (lbegin == lend) {
  2217.                      load_box_buff( p, block_start, bc, ec, ' ' );
  2218.                      len = last_c;
  2219.                   } else {
  2220.                      len = len - bc;
  2221.                      g_status.copied = TRUE;
  2222.  
  2223.                      assert( len >= 0 );
  2224.                      assert( len < MAX_LINE_LENGTH );
  2225.  
  2226.                      _fmemcpy( p, block_start->line + bc, len );
  2227.                   }
  2228.                }
  2229.             } else if (block_type == STREAM && l == 1L) {
  2230.                copy_line( block_start );
  2231.                detab_linebuff( );
  2232.                len = g_status.line_buff_len;
  2233.                if (len > ec + 1)
  2234.                   len = ec + 1;
  2235.             } else {
  2236.                copy_line( block_start );
  2237.                len = g_status.line_buff_len;
  2238.             }
  2239.  
  2240.             assert( len >= 0 );
  2241.             assert( len < MAX_LINE_LENGTH );
  2242.  
  2243.             *(p+len) = '\r';
  2244.             ++len;
  2245.             *(p+len) = '\n';
  2246.             ++len;
  2247.             if (fwrite( p, sizeof( char ), len, stdprn ) < (unsigned)len ||
  2248.                 ceh.flag == ERROR)
  2249.                col = ERROR;
  2250.             block_start = block_start->next;
  2251.             ++lbegin;
  2252.          }
  2253.          g_status.copied = FALSE;
  2254.          if (ceh.flag != ERROR)
  2255.             fflush( stdprn );
  2256.          else
  2257.             rc = ERROR;
  2258.       }
  2259.    }
  2260.    g_status.copied = FALSE;
  2261.    restore_screen_line( 0, prompt_line, line_buff );
  2262.    return( rc );
  2263. }
  2264.  
  2265.  
  2266. /*
  2267.  * Name:    get_block_fill_char
  2268.  * Class:   helper function
  2269.  * Purpose: get the character to fill marked block.
  2270.  * Date:    June 5, 1991
  2271.  * Passed:  window:  pointer to current window
  2272.  *          c: address of character to fill block
  2273.  */
  2274. int  get_block_fill_char( WINDOW *window, int *c )
  2275. {
  2276. char answer[MAX_COLS];
  2277. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  2278. register int col;
  2279. int prompt_line;
  2280. int rc;
  2281.  
  2282.    rc = OK;
  2283.    prompt_line = window->bottom_line;
  2284.    save_screen_line( 0, prompt_line, line_buff );
  2285.    /*
  2286.     * enter character to file block (esc to exit)
  2287.     */
  2288.  
  2289.    assert( strlen( block15 ) < MAX_COLS );
  2290.  
  2291.    strcpy( answer, block15 );
  2292.    s_output( answer, prompt_line, 0, g_display.message_color );
  2293.    col = strlen( answer );
  2294.    eol_clear( col, prompt_line, g_display.text_color );
  2295.    xygoto( col, prompt_line );
  2296.    col = getkey( );
  2297.    if (col >= 256)
  2298.       rc = ERROR;
  2299.    else
  2300.       *c = col;
  2301.    restore_screen_line( 0, prompt_line, line_buff );
  2302.    return( rc );
  2303. }
  2304.  
  2305.  
  2306. /*
  2307.  * Name:    get_block_numbers
  2308.  * Class:   helper function
  2309.  * Purpose: get the starting number and increment
  2310.  * Date:    June 5, 1991
  2311.  * Passed:  window:  pointer to current window
  2312.  *          block_num: address of number to start numbering
  2313.  *          block_inc: address of number to add to block_num
  2314.  *          just:      left or right justify numbers in block?
  2315.  */
  2316. int  get_block_numbers( WINDOW *window, long *block_num, long *block_inc,
  2317.                         int *just )
  2318. {
  2319. char answer[MAX_COLS];
  2320. int prompt_line;
  2321. register int rc;
  2322. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  2323. register int col;
  2324.  
  2325.    prompt_line = window->bottom_line;
  2326.  
  2327.    /*
  2328.     * don't assume anything on starting number - start w/ null string.
  2329.     */
  2330.    answer[0] = '\0';
  2331.    /*
  2332.     * enter starting number
  2333.     */
  2334.    rc = get_name( block16, prompt_line, answer, g_display.message_color );
  2335.    if (answer[0] == '\0')
  2336.       rc = ERROR;
  2337.    if (rc != ERROR) {
  2338.       *block_num = atol( answer );
  2339.  
  2340.       /*
  2341.        * assume increment is 1
  2342.        */
  2343.       answer[0] = '1';
  2344.       answer[1] = '\0';
  2345.       /*
  2346.        * enter increment
  2347.        */
  2348.       rc = get_name( block17, prompt_line, answer, g_display.message_color );
  2349.       if (answer[0] == '\0')
  2350.          rc = ERROR;
  2351.       if (rc != ERROR) {
  2352.          *block_inc = atol( answer );
  2353.  
  2354.          /*
  2355.           * now, get left or right justification.  save contents of screen
  2356.           *  in a buffer, then write contents of buffer back to screen when
  2357.           *  we get through w/ justification.
  2358.           */
  2359.          save_screen_line( 0, prompt_line, line_buff );
  2360.          /*
  2361.           * left or right justify (l/r)?
  2362.           */
  2363.  
  2364.          assert( strlen( block18 ) < MAX_COLS );
  2365.  
  2366.          strcpy( answer, block18 );
  2367.          s_output( answer, prompt_line, 0, g_display.message_color );
  2368.          col = strlen( answer );
  2369.          eol_clear( col, prompt_line, g_display.text_color );
  2370.          xygoto( col, prompt_line );
  2371.          rc = get_lr( );
  2372.          if (rc != ERROR) {
  2373.             *just = rc;
  2374.             rc = OK;
  2375.          }
  2376.          restore_screen_line( 0, prompt_line, line_buff );
  2377.       }
  2378.    }
  2379.  
  2380.    /*
  2381.     * if everything is everything then return code = OK.
  2382.     */
  2383.    return( rc );
  2384. }
  2385.  
  2386.  
  2387. /*
  2388.  * Name:    block_trim_trailing
  2389.  * Class:   primary editor function
  2390.  * Purpose: Trim trailing space in a LINE block.
  2391.  * Date:    June 5, 1991
  2392.  * Passed:  window:  pointer to current window
  2393.  * Notes:   Use copy_line and un_copy_line to do the work.
  2394.  */
  2395. int  block_trim_trailing( WINDOW *window )
  2396. {
  2397. int prompt_line;
  2398. int rc;
  2399. line_list_ptr p;             /* pointer to block line */
  2400. file_infos *file;
  2401. WINDOW *sw, s_w;
  2402. long er;
  2403. int  trailing;               /* save trailing setting */
  2404.  
  2405.    /*
  2406.     * make sure block is marked OK and that this is a LINE block
  2407.     */
  2408.    prompt_line = window->bottom_line;
  2409.    entab_linebuff( );
  2410.    rc = un_copy_line( window->ll, window, TRUE );
  2411.    check_block( );
  2412.    if (rc != ERROR && g_status.marked == TRUE) {
  2413.  
  2414.       trailing = mode.trailing;
  2415.       mode.trailing = TRUE;
  2416.       file = g_status.marked_file;
  2417.       if (file->block_type != LINE) {
  2418.          /*
  2419.           * can only trim trailing space in line blocks
  2420.           */
  2421.          error( WARNING, prompt_line, block21 );
  2422.          return( ERROR );
  2423.       }
  2424.  
  2425.       /*
  2426.        * initialize everything
  2427.        */
  2428.       sw = g_status.window_list;
  2429.       for (; ptoul( sw->file_info ) != ptoul( file );)
  2430.          sw = sw->next;
  2431.       if (mode.do_backups == TRUE) {
  2432.          file->modified = TRUE;
  2433.          rc = backup_file( sw );
  2434.       }
  2435.       dup_window_info( &s_w, sw );
  2436.  
  2437.       /*
  2438.        * set window to invisible so the un_copy_line function will
  2439.        * not display the lines while trimming.
  2440.        */
  2441.       s_w.visible = FALSE;
  2442.  
  2443.       p  = file->block_start;
  2444.       er = file->block_er;
  2445.       s_w.rline = file->block_br;
  2446.       for (; rc == OK && s_w.rline <= er  &&  !g_status.control_break; s_w.rline++) {
  2447.  
  2448.          /*
  2449.           * use the line buffer to trim space.
  2450.           */
  2451.          copy_line( p );
  2452.          rc = un_copy_line( p, &s_w, TRUE );
  2453.          p = p->next;
  2454.       }
  2455.  
  2456.       /*
  2457.        * IMPORTANT:  we need to reset the copied flag because the cursor may
  2458.        * not necessarily be on the last line of the block.
  2459.        */
  2460.       g_status.copied = FALSE;
  2461.       file->dirty = GLOBAL;
  2462.       mode.trailing = trailing;
  2463.       show_avail_mem( );
  2464.    }
  2465.    return( rc );
  2466. }
  2467.  
  2468.  
  2469. /*
  2470.  * Name:    block_email_reply
  2471.  * Class:   primary editor function
  2472.  * Purpose: insert the standard replay character '>' at beginning of line
  2473.  * Date:    June 5, 1992
  2474.  * Passed:  window:  pointer to current window
  2475.  * Notes:   it is customary to prepend "> " to the initial text and
  2476.  *             ">" to replies to replies to etc...
  2477.  */
  2478. int  block_email_reply( WINDOW *window )
  2479. {
  2480. int prompt_line;
  2481. int add;
  2482. int len;
  2483. int rc;
  2484. char *source;    /* source for block move to make room for c */
  2485. char *dest;      /* destination for block move */
  2486. line_list_ptr p;                     /* pointer to block line */
  2487. file_infos *file;
  2488. WINDOW *sw, s_w;
  2489. long er;
  2490.  
  2491.    /*
  2492.     * make sure block is marked OK and that this is a LINE block
  2493.     */
  2494.    prompt_line = window->bottom_line;
  2495.    entab_linebuff( );
  2496.    rc = un_copy_line( window->ll, window, TRUE );
  2497.    check_block( );
  2498.    if (rc != ERROR  &&  g_status.marked == TRUE) {
  2499.       file = g_status.marked_file;
  2500.       if (file->block_type != LINE) {
  2501.          /*
  2502.           * can only reply line blocks
  2503.           */
  2504.          error( WARNING, prompt_line, block25 );
  2505.          return( ERROR );
  2506.       }
  2507.  
  2508.       /*
  2509.        * find a window that points to the file with a marked block.
  2510.        */
  2511.       sw = g_status.window_list;
  2512.       for (; ptoul( sw->file_info ) != ptoul( file );)
  2513.          sw = sw->next;
  2514.       if (mode.do_backups == TRUE) {
  2515.          file->modified = TRUE;
  2516.          rc = backup_file( sw );
  2517.       }
  2518.  
  2519.       /*
  2520.        * use a local window structure to do the dirty work.  initialize
  2521.        *   the local window structure to the beginning of the marked
  2522.        *   block.
  2523.        */
  2524.       dup_window_info( &s_w, sw );
  2525.  
  2526.       /*
  2527.        * set s_w to invisible so the un_copy_line function will
  2528.        * not display the lines while doing block stuff.
  2529.        */
  2530.       s_w.visible = FALSE;
  2531.       s_w.rline = file->block_br;
  2532.       p  = file->block_start;
  2533.       er = file->block_er;
  2534.  
  2535.       /*
  2536.        * for each line in the marked block, prepend the reply character(s)
  2537.        */
  2538.       for (; rc == OK  &&  s_w.rline <= er  &&  !g_status.control_break;
  2539.                                                              s_w.rline++) {
  2540.  
  2541.          /*
  2542.           * put the line in the g_status.line_buff.  use add to count the
  2543.           *   number of characters to insert at the beginning of a line.
  2544.           *   the original reply uses "> ", while replies to replies use ">".
  2545.           */
  2546.          copy_line( p );
  2547.          if (*(p->line) == '>')
  2548.             add = 1;
  2549.          else
  2550.             add = 2;
  2551.  
  2552.          /*
  2553.           * see if the line has room to add the ">" character.  if there is
  2554.           *   room, move everything down to make room for the
  2555.           *   reply character(s).
  2556.           */
  2557.          len = g_status.line_buff_len;
  2558.          if (len + add < MAX_LINE_LENGTH) {
  2559.             source = g_status.line_buff;
  2560.             dest = source + add;
  2561.  
  2562.             assert( len >= 0 );
  2563.             assert( len < MAX_LINE_LENGTH );
  2564.             assert( len + add < MAX_LINE_LENGTH );
  2565.  
  2566.             memmove( dest, source, len );
  2567.             *source = '>';
  2568.             if (add > 1)
  2569.               *(source+1) = ' ';
  2570.             g_status.line_buff_len = len + add;
  2571.             rc = un_copy_line( p, &s_w, TRUE );
  2572.          }
  2573.          p = p->next;
  2574.          g_status.copied = FALSE;
  2575.       }
  2576.  
  2577.       /*
  2578.        * IMPORTANT:  we need to reset the copied flag because the cursor may
  2579.        * not necessarily be on the last line of the block.
  2580.        */
  2581.       g_status.copied = FALSE;
  2582.       file->dirty = GLOBAL;
  2583.       show_avail_mem( );
  2584.    }
  2585.    return( OK );
  2586. }
  2587.  
  2588.  
  2589. /*
  2590.  * Name:    block_convert_case
  2591.  * Class:   primary editor function
  2592.  * Purpose: convert characters to lower case, upper case, strip hi bits,
  2593.  *          or e-mail functions
  2594.  * Date:    June 5, 1991
  2595.  * Passed:  window:  pointer to current window
  2596.  */
  2597. int  block_convert_case( WINDOW *window )
  2598. {
  2599. int  len;
  2600. int  block_type;
  2601. line_list_ptr begin;
  2602. register file_infos *file;
  2603. WINDOW *sw;
  2604. long number;
  2605. long er;
  2606. unsigned int count;
  2607. int  bc, ec;
  2608. int  block_len;
  2609. int  rc;
  2610. void (*char_func)( text_ptr, unsigned int );
  2611.  
  2612.    /*
  2613.     * make sure block is marked OK
  2614.     */
  2615.    entab_linebuff( );
  2616.    if (un_copy_line( window->ll, window, TRUE ) == ERROR)
  2617.       return( ERROR );
  2618.    rc = OK;
  2619.    check_block( );
  2620.    if (g_status.marked == TRUE) {
  2621.  
  2622.       /*
  2623.        * set char_func() to the required block function in tdeasm.c
  2624.        */
  2625.       switch (g_status.command) {
  2626.          case BlockUpperCase  :
  2627.             char_func = upper_case;
  2628.             break;
  2629.          case BlockLowerCase  :
  2630.             char_func = lower_case;
  2631.             break;
  2632.          case BlockRot13      :
  2633.             char_func = rot13;
  2634.             break;
  2635.          case BlockFixUUE     :
  2636.             char_func = fix_uue;
  2637.             break;
  2638.          case BlockStripHiBit :
  2639.             char_func = strip_hi;
  2640.             break;
  2641.          default :
  2642.             return( ERROR );
  2643.       }
  2644.  
  2645.       file  = g_status.marked_file;
  2646.       file->modified = TRUE;
  2647.       if (mode.do_backups == TRUE) {
  2648.          sw = g_status.window_list;
  2649.          for (; ptoul( sw->file_info ) != ptoul( file );)
  2650.             sw = sw->next;
  2651.          rc = backup_file( sw );
  2652.       }
  2653.  
  2654.       if (rc == OK) {
  2655.          block_type = file->block_type;
  2656.          ec = file->block_ec;
  2657.  
  2658.          begin  = file->block_start;
  2659.  
  2660.          er = file->block_er;
  2661.          block_len = ec + 1 - file->block_bc;
  2662.          for (number=file->block_br; number <= er; number++) {
  2663.             begin->dirty = TRUE;
  2664.             count = len = begin->len;
  2665.             bc = 0;
  2666.             if (block_type == STREAM) {
  2667.                if (number == file->block_br) {
  2668.                   bc = file->block_bc;
  2669.                   if (len < file->block_bc) {
  2670.                      count = 0;
  2671.                      bc = len;
  2672.                   }
  2673.                }
  2674.                if (number == file->block_er) {
  2675.                   if (ec < len)
  2676.                      ec = len;
  2677.                   count = ec - bc + 1;
  2678.                }
  2679.             } else if (block_type == BOX) {
  2680.                bc = file->block_bc;
  2681.                count =  len >= ec ? block_len : len - bc;
  2682.             }
  2683.             if (len > bc) {
  2684.  
  2685.                assert( count < MAX_LINE_LENGTH );
  2686.                assert( bc >= 0 );
  2687.                assert( bc < MAX_LINE_LENGTH );
  2688.  
  2689.                (*char_func)( begin->line+bc, count );
  2690.             }
  2691.             begin = begin->next;
  2692.          }
  2693.  
  2694.          /*
  2695.           * IMPORTANT:  we need to reset the copied flag because the cursor may
  2696.           * not necessarily be on the last line of the block.
  2697.           */
  2698.          g_status.copied = FALSE;
  2699.          file->dirty = GLOBAL;
  2700.       }
  2701.    } else
  2702.       rc = ERROR;
  2703.    return( rc );
  2704. }
  2705.  
  2706.  
  2707. /*
  2708.  * Name:    upper_case
  2709.  * Purpose: To convert all lower case characters to upper characters
  2710.  * Date:    June 5, 1991
  2711.  * Passed:  s:    the starting point
  2712.  *          count: number of characters to convert
  2713.  * Returns: none
  2714.  * Notes:   xor 0x20 with lower case to get upper case.  yes, I know
  2715.  *           the toupper( ) macro or function is faster, but let's
  2716.  *           let make it easy for users to modify for non-English alphabets.
  2717.  *          this routine only handles the English alphabet.  modify as
  2718.  *           needed for other alphabets.
  2719.  */
  2720. void upper_case( text_ptr s, size_t count )
  2721. {
  2722.    if (s != NULL) {
  2723.       for (; count > 0; s++, count-- ) {
  2724.          if (*s >= 'a'  &&  *s <= 'z')
  2725.             *s ^= 0x20;
  2726.       }
  2727.    }
  2728. }
  2729.  
  2730.  
  2731. /*
  2732.  * Name:    lower_case
  2733.  * Purpose: To convert all upper case characters to lower characters
  2734.  * Date:    June 5, 1991
  2735.  * Passed:  s:    the starting point
  2736.  *          count: number of characters to convert
  2737.  * Returns: none
  2738.  * Notes:   or upper case with 0x20 to get lower case.  yes, I know
  2739.  *           the tolower( ) macro or function is faster, but let's
  2740.  *           let make it easy for users to modify for non-English alphabets. 
  2741.  *          this routine only handles the English alphabet.  modify as
  2742.  *           needed for other alphabets.
  2743.  */
  2744. void lower_case( text_ptr s, size_t count )
  2745. {
  2746.    if (s != NULL) {
  2747.       for (; count > 0; s++, count-- ) {
  2748.          if (*s >= 'A'  &&  *s <= 'Z')
  2749.             *s |= 0x20;
  2750.       }
  2751.    }
  2752. }
  2753.  
  2754.  
  2755. /*
  2756.  * Name:    rot13
  2757.  * Purpose: To rotate all alphabet characters by 13
  2758.  * Date:    June 5, 1991
  2759.  * Passed:  s:    the starting point
  2760.  *          count: number of characters to convert
  2761.  * Returns: none
  2762.  * Notes:   simple rot13
  2763.  *          i really don't know how to handle rot13 for alphabets
  2764.  *           other than English.
  2765.  */
  2766. void rot13( text_ptr s, size_t count )
  2767. {
  2768. register size_t c;
  2769.  
  2770.    c = *s;
  2771. }
  2772.  
  2773.  
  2774. /*
  2775.  * Name:    fix_uue
  2776.  * Purpose: To fix EBCDIC ==> ASCII translation problem
  2777.  * Date:    June 5, 1991
  2778.  * Passed:  s:    the starting point
  2779.  *          count: number of characters to convert
  2780.  * Returns: none
  2781.  * Notes:   to fix the EBCDIC to ASCII translation problem, three characters
  2782.  *           need to be changed,  0x5d -> 0x7c, 0xd5 -> 0x5b, 0xe5 -> 0x5d
  2783.  */
  2784. void fix_uue( text_ptr s, size_t  count )
  2785. {
  2786.    if (s != NULL) {
  2787.       for (; count > 0; s++, count-- ) {
  2788.          switch (*s) {
  2789.             case 0x5d :
  2790.                *s = 0x7c;
  2791.                break;
  2792.             case 0xd5 :
  2793.                *s = 0x5b;
  2794.                break;
  2795.             case 0xe5 :
  2796.                *s = 0x5d;
  2797.                break;
  2798.             default :
  2799.                break;
  2800.          }
  2801.       }
  2802.    }
  2803. }
  2804.  
  2805.  
  2806. /*
  2807.  * Name:    strip_hi
  2808.  * Purpose: To strip bit 7 from characters
  2809.  * Date:    June 5, 1991
  2810.  * Passed:  s:    the starting point, which should be normalized to a segment
  2811.  *          count: number of characters to strip (size_t)
  2812.  *                 count should not be greater than MAX_LINE_LENGTH
  2813.  * Returns: none
  2814.  * Notes:   this function is useful on WordStar files.  to make a WordStar
  2815.  *           file readable, the hi bits need to be anded off.
  2816.  *          incidentally, soft CRs and LFs may be converted to hard CRs
  2817.  *           and LFs.  this function makes no attempt to split lines
  2818.  *           when soft returns are converted to hard returns.
  2819.  */
  2820. void strip_hi( text_ptr s, size_t count )
  2821. {
  2822.    if (s != NULL) {
  2823.       for (; count > 0; s++, count-- ) {
  2824.          if (*s >= 0x80)
  2825.             *s &= 0x7f;
  2826.       }
  2827.    }
  2828. }
  2829.