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