home *** CD-ROM | disk | FTP | other *** search
/ Shareware 1 2 the Maxx / sw_1.zip / sw_1 / EDITORS / TDE20.ZIP / BLOCK.C next >
C/C++ Source or Header  |  1992-06-05  |  80KB  |  2,363 lines

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