home *** CD-ROM | disk | FTP | other *** search
/ Amiga Elysian Archive / AmigaElysianArchive.iso / wp_dtp / xdme1820.lha / XDME / block.c < prev    next >
C/C++ Source or Header  |  1993-03-09  |  33KB  |  1,445 lines

  1. /******************************************************************************
  2.  
  3.     MODUL
  4.     block.c
  5.  
  6.     DESCRIPTION
  7.     In this module are all functions that work with blocks.
  8.  
  9.     NOTES
  10.     - A block has these attributes:
  11.         1- the editor it is in
  12.         2- the type (BT_NONE, BT_LINE, BT_NORMAL or BT_VERTICAL)
  13.            if it is BT_NONE, there is no block.
  14.         3- a start-position (line/column). If the type is not BT_NONE,
  15.            this field is guranteed to contain a valid position.
  16.         4- an end-position. If the line is -1, the end-position is not
  17.            yet defined.
  18.  
  19. ******************************************************************************/
  20.  
  21. /**************************************
  22.         Includes
  23. **************************************/
  24. #include <defs.h>
  25. #include "clipboard.h"
  26. #define MYDEBUG     1
  27. #include "debug.h"
  28.  
  29.  
  30. /**************************************
  31.         Globale Variable
  32. **************************************/
  33. Prototype ushort is_inblock     (Line, Column);
  34. Prototype void     displayblock     (Bool);
  35. Prototype void     redraw_block     (Bool, Line, Column, Line, Column);
  36. Prototype void     do_blocktype     (void);
  37. Prototype void     do_block     (void);
  38. Prototype Bool     block_ok     (void);
  39. Prototype void     do_copy     (void);
  40. Prototype char * block_to_string (void);
  41. Prototype void     do_bdelete     (void);
  42. Prototype void     do_bcopy     (void);
  43. Prototype void     do_bmove     (void);
  44. Prototype void     do_bsource     (void);
  45.  
  46.  
  47. /**************************************
  48.       Interne Defines & Strukturen
  49. **************************************/
  50. #define SWAP(a,b)   (void)((a)^=(b),(b)^=(a),(a)^=(b))
  51. #define MIN(a,b)    ((a) <= (b) ? (a) : (b))
  52. #define MAX(a,b)    ((a) >= (b) ? (a) : (b))
  53.  
  54. #define BF_LINESWAP        1L
  55. #define BF_COLUMNSWAP        2L
  56.  
  57.  
  58. /**************************************
  59.         Interne Variable
  60. **************************************/
  61. static ushort block_type = BT_LINE;    /* default-type of block */
  62.  
  63.  
  64. /**************************************
  65.        Interne Prototypes
  66. **************************************/
  67.  
  68.  
  69. /*****************************************************************************
  70.  
  71.     NAME
  72.     is_inblock
  73.  
  74.     PARAMETER
  75.     Line   line;        The line we want to check
  76.     Column column;        the column we are in. If column == -1,
  77.                 we just check the line.
  78.  
  79.     RETURN
  80.     ushort blockpos;    Flags:
  81.  
  82.             BP_OUTSIDE        we are not inside any block.
  83.             BP_START        we are in the line where the block starts.
  84.             BP_END        we are in the line where the block ends.
  85.             BP_INSIDE        we are really inside the block.
  86.  
  87.     DESCRIPTION
  88.     This function checks whether a position is inside a block.
  89.     If it isn't, we get BP_OUTSIDE (= 0). In the other case, we
  90.     have several flags. If line is the start-line of the block,
  91.     BP_START is set. The same goes for the end-line.
  92.         Normal and vertical blocks also check for the column.
  93.     If we don't care for this, we may simply specify -1L for the
  94.     column. Else, we give the column we want to check and get an
  95.     additional BP_INSIDE if we are really inside the block.
  96.  
  97.     EXAMPLE
  98.  
  99.     BP_OUTSIDE    there is no block in that line
  100.  
  101.     BP_START    the line is the start-line of the block. Also the
  102.             block has more than one line since BP_END is not
  103.             set. The block is not a line-block (BP_INSIDE is
  104.             always true for line-blocks !).
  105.  
  106.     BP_START|BP_END The block has only one line and we are in it. This is
  107.             also no line-block.
  108.  
  109.     BP_START|BP_INSIDE We are inside the block and on it's start-line.
  110.  
  111.     BP_INSIDE    this position is inside the block, but not in the
  112.             start- nor in the end-line.
  113.  
  114. ******************************************************************************/
  115.  
  116. ushort is_inblock (Line line, Column col)
  117. {
  118.     ushort blockpos = BP_OUTSIDE;
  119.  
  120.     /* Look if there is a block, the actual editor is the one that
  121.        has it, we are not in a commandline and the line is inside it.
  122.        NOTE: if there is no end-line yet, the last condition can never
  123.        be TRUE ! */
  124.  
  125.     if (    ActualBlock.type != BT_NONE     &&
  126.         Ep == ActualBlock.ep        &&
  127.         !globalflags.Comlinemode        &&
  128.         line >= ActualBlock.start_line  &&
  129.         line <= ActualBlock.end_line     )
  130.     {
  131.  
  132.     /* Check for start- and end-line */
  133.  
  134.     if (line == ActualBlock.start_line)
  135.         blockpos |= BP_START;
  136.  
  137.     if (line == ActualBlock.end_line)
  138.         blockpos |= BP_END;
  139.  
  140.     /* now check, if we are inside the block */
  141.  
  142.     switch (ActualBlock.type)
  143.     {
  144.     case BT_LINE:
  145.         /* in a line-block, we are always inside */
  146.         blockpos |= BP_INSIDE;
  147.     break;
  148.  
  149.     case BT_NORMAL:
  150.     {
  151.         BOOL after_start, before_end;
  152.  
  153.         /* in a normal block, we must also check the column.
  154.         In the startline, we are inside the block if we are to the
  155.         left of the startcolumn. In the endline, we must be to
  156.         the right. */
  157.         /* NOTE that we check the lines above ! */
  158.  
  159.         if (line > ActualBlock.start_line ||
  160.             (col == -1 || col >= ActualBlock.start_column)
  161.            )
  162.         after_start = TRUE;
  163.         else
  164.         after_start = FALSE;
  165.  
  166.         if (line < ActualBlock.end_line ||
  167.             (col == -1 || col <= ActualBlock.end_column ||
  168.              ActualBlock.end_column == (LINELEN(Ep,line)) )
  169.            )
  170.         before_end = TRUE;
  171.         else
  172.         before_end = FALSE;
  173.  
  174.         if (after_start && before_end)
  175.         blockpos |= BP_INSIDE;
  176.     }
  177.     break;
  178.  
  179.     case BT_VERTICAL:
  180.         /* in a vertical block, we must be between the start- and the
  181.         end-column */
  182.  
  183.         if ((     col  == -1                          ||
  184.             ( col  >= ActualBlock.start_column &&
  185.               col  <= ActualBlock.end_column ) ) &&
  186.               line >= ActualBlock.start_line  &&
  187.               line <= ActualBlock.end_line      )
  188.         blockpos |= BP_INSIDE;
  189.     break;
  190.     } /* switch (ActualBlock.type) */
  191.     } /* if validblock */
  192.  
  193.     /* return the flags */
  194.  
  195.     return (blockpos);
  196. } /* is_inblock */
  197.  
  198.  
  199. /*****************************************************************************
  200.  
  201.     NAME
  202.     displayblock
  203.  
  204.     PARAMETER
  205.     Bool on;    Shall we turn the block on ?
  206.  
  207.     RETURN
  208.     void
  209.  
  210.     DESCRIPTION
  211.     This function displays the block. If on is TRUE, the block
  212.     is drawn (again), else we turn the block off. In this case, the
  213.     block-selection is lost !
  214.  
  215. ******************************************************************************/
  216.  
  217. void displayblock (Bool on)
  218. {
  219.     if (on)
  220.     {
  221.     /* draw block and write the text over it */
  222.     redraw_block (TRUE,
  223.             Ep->topline, Ep->topcolumn,
  224.             Ep->topline + Lines - 1, Ep->topcolumn + Columns - 1);
  225.  
  226.     } else if (block_ok ()) /* turn block off AND there was a block */
  227.     {
  228.     ED   * ep = Ep;
  229.     RP   * rp = ep->win->RPort;
  230.     ushort start;
  231.     ushort end;
  232.  
  233.     /* no block anymore */
  234.  
  235.     ActualBlock.type = BT_NONE;
  236.     ActualBlock.ep     = NULL;
  237.  
  238.     /* block is not visible */
  239.     if (ActualBlock.end_line < ep->topline)
  240.         return;
  241.  
  242.     /* start-line: If block starts above the 1st visible line,
  243.        we start in line 0 else in the line where the block starts. */
  244.  
  245.     if (ActualBlock.start_line < ep->topline)
  246.         start = ep->topline;
  247.     else
  248.         start = ActualBlock.start_line;
  249.  
  250.     /* is end-line visible ? */
  251.     if (ep->topline + Lines > ActualBlock.end_line)
  252.         end = ActualBlock.end_line;
  253.     else
  254.         /* only upto the end of the display */
  255.         end = ep->topline + Lines -1;
  256.  
  257.     if (start <= end)
  258.     {
  259.         /* color and mask */
  260.  
  261.         SetAPen (rp, TEXT_BPEN);
  262.         SetWrMsk (rp, BLOCK_MASK);
  263.  
  264.         /* clear area */
  265.         RectFill (rp, Xbase, ROW(start - Ep->topline), Xpixs,
  266.             ROW(end-Ep->topline + 1)-1);
  267.  
  268.         /* redraw text */
  269.         redraw_block (TRUE, start, ep->topcolumn,
  270.         end, ep->topcolumn + Columns -1);
  271.     }
  272.     } else /* just say "there is no block" */
  273.     ActualBlock.type = BT_NONE;
  274.  
  275. } /* displayblock */
  276.  
  277.  
  278. /*****************************************************************************
  279.  
  280.     NAME
  281.     do_blocktype
  282.  
  283.     PARAMETER
  284.     av[0] : blocktype
  285.     av[1] : LINE/CHARACTER/NORMAL/VERTICAL
  286.  
  287.     RETURN
  288.     void
  289.  
  290.     DESCRIPTION
  291.     This sets the type of block you want to use. Allowed are 3 types
  292.     of blocks:
  293.  
  294.         LINE:    The line-block begins in a line and ends in a line.
  295.             The columns are ignored and both lines are included
  296.             in the block.
  297.         CHARCTER or NORMAL: The character-block begins at a specifiy line
  298.             and columns and ends at another character-position.
  299.         VERTICAL:    The vertical block is a rectangular region of text
  300.             like a column of a table.
  301.  
  302.     If you have already marked a block, it is converted to the new type.
  303.  
  304. ******************************************************************************/
  305.  
  306. void do_blocktype (void)
  307. {
  308.     switch (av[1][0])
  309.     {
  310.     case 'l':
  311.     case 'L':
  312.     block_type = BT_LINE;
  313.     break;
  314.  
  315.     case 'c':
  316.     case 'C':
  317.     case 'n':
  318.     case 'N':
  319.     block_type = BT_NORMAL;
  320.     break;
  321.  
  322.     case 'v':
  323.     case 'V':
  324.     block_type = BT_VERTICAL;
  325.     break;
  326.     }
  327.  
  328.     /* check if there is a block and convert the type */
  329.     if (block_ok() && ActualBlock.type != block_type)
  330.     {
  331.     /* set new type */
  332.     ActualBlock.type = block_type;
  333.  
  334.     if (block_type == BT_NORMAL)
  335.     {
  336.         int linelen;
  337.  
  338.         /* check start and end-columns */
  339.         linelen = LINELEN(Ep,ActualBlock.start_line);
  340.  
  341.         if (linelen < ActualBlock.start_column)
  342.         ActualBlock.start_column = linelen;
  343.  
  344.         linelen = LINELEN(Ep,ActualBlock.end_line);
  345.  
  346.         if (linelen < ActualBlock.end_column)
  347.         ActualBlock.end_column = linelen;
  348.     }
  349.  
  350.     /* clean screen and force redraw */
  351.     text_redisplay ();
  352.     }
  353. } /* do_blocktype */
  354.  
  355.  
  356. /*****************************************************************************
  357.  
  358.     NAME
  359.     do_block
  360.  
  361.     PARAMETER
  362.     av[0] : block unblock bstart bend lineblock
  363.  
  364.     RETURN
  365.     void
  366.  
  367.     DESCRIPTION
  368.     This routine allows to specify the limits of a block.
  369.  
  370.     The two commands BSTART and BEND can be used anywhere in the
  371.     text (ie. you can user BEND above a BSTART). In this case,
  372.     BEND and BSTART swap their meanings, ie. BEND now sets the start
  373.     of the block and BSTART the end. This is for specifying a block
  374.     with the mouse. If you drag the mouse above the BSTART, the BEND
  375.     will begin to set the beginning of the block and if you drag it
  376.     down over the end of the block, BEND will switch back.
  377.  
  378. ******************************************************************************/
  379.  
  380. void do_block (void)
  381. {
  382.     ED * ep = Ep;
  383.  
  384.     /* make sure we work on the most recent version of the text */
  385.  
  386.     text_sync ();
  387.  
  388.     /* look for the 1st character */
  389.  
  390.     switch(av[0][0])
  391.     {
  392.     case 'b': /* Block Bstart Bend */
  393.         switch (av[0][1]) /* b[lse] */
  394.         {
  395.         case 'l': /* BLock */
  396.             /* if there is no block yet, we set the start-line */
  397.  
  398.             if (ActualBlock.type == BT_NONE)
  399.             {
  400. bstart:
  401.             /* set the editor, the start-line and the type. To
  402.                make clear the end-line is not valid yet, we set
  403.                it to -1 */
  404.  
  405.             ActualBlock.ep         = ep;
  406.             ActualBlock.type     = block_type;
  407.             ActualBlock.start_line     = ep->line;
  408.             ActualBlock.start_column = ep->column;
  409.             ActualBlock.end_line     = -1;
  410.             ActualBlock.flags     = 0;
  411.  
  412.             title ("Block begin");
  413.             } else
  414.             {
  415.             /* if there is a block */
  416.  
  417.             if (block_ok ())
  418.             {
  419.                 /* shall we automagically unblock ? */
  420.  
  421.                 if (ep->config.autounblock)
  422.                 {
  423.                 /* trun block off */
  424.  
  425.                 text_redrawblock (0);
  426.  
  427.                 /* set start */
  428.  
  429.                 goto bstart;
  430.                 } else
  431.                 {
  432.                 /* no auto-unblock ? ERROR ! */
  433.  
  434.                 error ("block:\nBlock already marked");
  435.  
  436.                 break;
  437.                 } /* autounblock */
  438.             } /* if there is already a block */
  439.  
  440.             /* The last BLOCK began in another editor: Restart ! */
  441.  
  442.             if (ActualBlock.ep != ep)
  443.                 goto bstart;
  444.  
  445.             ActualBlock.end_line   = ep->line;
  446.             ActualBlock.end_column = ep->column;
  447.  
  448.             /* now, the block is ready ! */
  449.  
  450.             title ("Block end");
  451.  
  452.             text_redrawblock (1);       /* Now display */
  453.             }
  454.         break;
  455.  
  456.         case 's': /* BStart */
  457.             /* no block yet or the old block was in another editor ? */
  458.             if (ActualBlock.type == BT_NONE || ActualBlock.ep != ep)
  459.             {
  460.             /* is there an old block and the editor that contains
  461.                the block is not iconified */
  462.             if (ActualBlock.type != BT_NONE &&
  463.                 !ActualBlock.ep->iconmode)
  464.             {
  465.                 /* change editor */
  466.                 switch_ed (ActualBlock.ep);
  467.  
  468.                 /* switch block off */
  469.                 text_redrawblock (0);
  470.  
  471.                 /* come back */
  472.                 switch_ed (ep);
  473.             }
  474.  
  475.             goto bstart;
  476.             }
  477.  
  478.             if (ActualBlock.end_line != -1)
  479.             {
  480.             redraw_block (FALSE,
  481.                     ep->line,
  482.                     ep->column,
  483.                     -1, -1);
  484.             } else
  485.             {
  486.             ActualBlock.start_line     = ep->line;
  487.             ActualBlock.start_column = ep->column;
  488.  
  489.             title ("Block begin");
  490.             }
  491.         break;
  492.  
  493.         case 'e': /* bend */
  494.             /* It is not possible to use BEND if there is no
  495.                start yet or the block is in another editor */
  496.  
  497.             if (ActualBlock.type == BT_NONE)
  498.             {
  499.             error ("bend:\n You must use BLOCK or\nBSTART first");
  500.             break;
  501.             } else if (ActualBlock.ep != ep)
  502.             {
  503.             error ("bend:\nYou cannot set the end for\n"
  504.                    "a block in another editor !");
  505.             break;
  506.             }
  507.  
  508.             /* say what we do */
  509.             title ("Block end");
  510.  
  511.             /* if there was no end yet, just set it and force
  512.             a redisplay */
  513.  
  514.             if (ActualBlock.end_line == -1)
  515.             {
  516.             ActualBlock.end_line   = ep->line;
  517.             ActualBlock.end_column = ep->column;
  518.  
  519.             text_redrawblock (1);   /* Now display */
  520.             } else
  521.             {
  522.             redraw_block (FALSE,
  523.                     -1, -1,
  524.                     ep->line, ep->column);
  525.             }
  526.         break;
  527.         }
  528.     break;
  529.  
  530.     case 'u':
  531.         /* force UNDRAW and say what we did */
  532.  
  533.         text_redrawblock (0);
  534.  
  535.         title ("Block unmarked");
  536.     break;
  537.  
  538.     case 'l':
  539.         /* mark the whole line as a block */
  540.  
  541.         ActualBlock.ep         = ep;
  542.         ActualBlock.type         = BT_NORMAL;
  543.         ActualBlock.start_line   =
  544.         ActualBlock.end_line = ep->line;
  545.         ActualBlock.start_column = 0;
  546.         ActualBlock.end_column   = Clen;
  547.         ActualBlock.flags         = 0;
  548.  
  549.         title ("Line marked");
  550.  
  551.         /* force redraw */
  552.  
  553.         text_redrawblock (1);
  554.     break;
  555.     }
  556. } /* do_block */
  557.  
  558.  
  559. /*****************************************************************************
  560.  
  561.     NAME
  562.     block_ok
  563.  
  564.     PARAMETER
  565.     void
  566.  
  567.     RETURN
  568.     Bool
  569.  
  570.     DESCRIPTION
  571.     Returns TRUE, if there is a valid block.
  572.  
  573. ******************************************************************************/
  574.  
  575. Bool block_ok (void)
  576. {
  577.     /* A block is valid, if it has a type and a valid end-line */
  578.  
  579.     return (ActualBlock.type != BT_NONE && ActualBlock.end_line >= 0);
  580. } /* block_ok */
  581.  
  582.  
  583. void test (void)
  584. {
  585. } /* test */
  586.  
  587.  
  588. /*****************************************************************************
  589.  
  590.     NAME
  591.     block_to_string
  592.  
  593.     PARAMETER
  594.     void
  595.  
  596.     RETURN
  597.     char * string;
  598.  
  599.     DESCRIPTION
  600.     Creates a string from the currently marked block. The lines
  601.     are separated with '\n' and the string is terminated with
  602.     '\0'.
  603.  
  604.     NOTES
  605.     - The string will only contain '\n's, if the end of a line is
  606.       included in the block.
  607.     - The block has to be freed with free() if you don't need it anymore,
  608.       but this may change.
  609.  
  610.     HISTORY
  611.     13. Feb 1993    ada created
  612.  
  613. ******************************************************************************/
  614.  
  615. void do_copy (void)
  616. {
  617.     struct IOClipReq * ior;
  618.     char * str = block_to_string ();
  619.  
  620.     if (str)
  621.     {
  622.     D(bug("block_to_string:\n`%s'\n", str));
  623.  
  624.     if (ior = CBOpen (0))
  625.     {
  626.         CBWriteFTXT (ior, str);
  627.  
  628.         CBClose (ior);
  629.     }
  630.  
  631.     free (str);
  632.     }
  633.     else
  634.     D(bug("block_to_string: (NUL)\n"));
  635. } /* do_copy */
  636.  
  637.  
  638. char * block_to_string (void)
  639. {
  640.     ED     * ep;
  641.     Line   line;
  642.     int    length;
  643.     int    linelen;
  644.     char * string,
  645.      * ptr,
  646.      * text;
  647.  
  648.     if (block_ok ())
  649.     {
  650.     ep = ActualBlock.ep;
  651.  
  652.     /* to prevent us from write the same code twice (once for
  653.        figuring out how long the string will be and once for actually
  654.        filling the string), we simply check if "string" is NULL.
  655.        If it is, we have to get the length else we copy the contents. */
  656.  
  657.     string = NULL;
  658.  
  659.     /*
  660.        LINE: The length of the block is the sum of the lengths of all
  661.         lines + CR + \0.
  662.  
  663.        NORMAL: The length of this block is the sum of the lengths of all
  664.         body lines plus the length of the start- and the end-line
  665.         plus \0. The length of the start-line is
  666.  
  667.             o 1, if length < start_column
  668.             o length-start_column+1, if length > start_column
  669.  
  670.         and the length of the end-line is
  671.  
  672.             o end_column+1, if length > end_column
  673.             o length+1, if length < end_column
  674.  
  675.         If the block has only one line, the length is
  676.  
  677.             o end_column-start_column if length > end_col
  678.             o length-start_column+1 if length < end_col
  679.  
  680.        VERTICAL: The length of the block is the sum of the lengths of
  681.         all lines + CR + \0. The length of a line is
  682.  
  683.             o 0 if the line is < start-column
  684.             o length-start_column if the line is < end-column
  685.             o start_column-end_column if line is >= end-column
  686.  
  687.        When we have the length, we allocate memory for it and copy
  688.        the block into that memory.
  689.      */
  690.  
  691.     length = 1;    /* ending '\0' */
  692.  
  693.     text_sync ();   /* use latest text */
  694.  
  695.     do {
  696.         for (line=ActualBlock.start_line; line <= ActualBlock.end_line;
  697.                                        line ++)
  698.         {
  699.         text = GETTEXT (ep,line);
  700.         linelen = strlen (text);
  701.  
  702.         if (linelen > 256)
  703.         {
  704.             D(bug("block_to_string: Aborting..."
  705.                 "len (%ld) in line %ld too large !\n",
  706.                 linelen, line));
  707.             break;
  708.         }
  709.  
  710.         if (string && ptr-string > length)
  711.             break;
  712.  
  713.         switch (ActualBlock.type)
  714.         {
  715.         case BT_LINE:
  716.             {
  717.             if (string)
  718.             {
  719.                 strcpy (ptr, text);
  720.                 ptr += linelen;
  721.  
  722.                 *ptr ++ = '\n';     /* always add LF */
  723.             }
  724.             else
  725.                 length += linelen + 1;
  726.             }
  727.         break;
  728.  
  729.         case BT_NORMAL:
  730.             if (line != ActualBlock.start_line)
  731.             {
  732.             if (line != ActualBlock.end_line)
  733.             {
  734.                 /* body line */
  735.  
  736.                 if (string)
  737.                 {
  738.                 strcpy (ptr, text);
  739.                 ptr += linelen;
  740.  
  741.                 *ptr ++ = '\n';     /* always add LF */
  742.                 }
  743.                 else
  744.                 length += linelen + 1;
  745.             }
  746.             else
  747.             {
  748.                 /* end-line */
  749.                 if (string)
  750.                 {
  751.                 strncpy (ptr, text, ActualBlock.end_column);
  752.                 ptr += ActualBlock.end_column;
  753.                 }
  754.                 else
  755.                 length += ActualBlock.end_column;
  756.  
  757.                 if (linelen == ActualBlock.end_column)
  758.                 {
  759.                 if (string)
  760.                     *ptr ++ = '\n';
  761.                 else
  762.                     length ++;
  763.                 }
  764.             }
  765.             }
  766.             else if (line != ActualBlock.end_line)
  767.             {
  768.             /* start-line */
  769.             if (linelen > ActualBlock.start_column)
  770.             {
  771.                 if (string)
  772.                 {
  773.                 strcpy (ptr, text + ActualBlock.start_column);
  774.                 ptr += strlen (ptr);
  775.                 }
  776.                 else
  777.                 length += linelen - ActualBlock.start_column;
  778.             }
  779.  
  780.             if (string)
  781.                 *ptr ++ = '\n';
  782.             else
  783.                 length ++;
  784.             }
  785.             else /* block has only one line */
  786.             {
  787.             if (linelen > ActualBlock.end_column)
  788.             {
  789.                 /* copy part of the line */
  790.  
  791.                 linelen = ActualBlock.end_column -
  792.                         ActualBlock.start_column + 1;
  793.  
  794.                 if (string)
  795.                 {
  796.                 strncpy (ptr, text + ActualBlock.start_column,
  797.                     linelen);
  798.                 ptr += linelen;
  799.                 }
  800.                 else
  801.                 length += linelen;
  802.             }
  803.             else if (linelen > ActualBlock.start_column)
  804.             {
  805.                 /* end of line + CR */
  806.                 if (string)
  807.                 {
  808.                 strcpy (ptr, text + ActualBlock.start_column);
  809.                 ptr += strlen (ptr);
  810.  
  811.                 *ptr ++ = '\n';
  812.                 }
  813.                 else
  814.                 {
  815.                 linelen -= ActualBlock.start_column;
  816.  
  817.                 length += linelen + 1;
  818.                 }
  819.             }
  820.             else
  821.             {
  822.                 /* only CR */
  823.  
  824.                 if (string)
  825.                 *ptr ++ = '\n';
  826.                 else
  827.                 length ++;
  828.             }
  829.             }
  830.         break;
  831.  
  832.         case BT_VERTICAL:
  833.         {
  834.             if (linelen <= ActualBlock.start_column)
  835.             {
  836.             /* only '\n' */
  837.             if (string)
  838.                 *ptr ++ = '\n';
  839.             else
  840.                 length ++;
  841.             }
  842.             else if (linelen <= ActualBlock.end_column)
  843.             {
  844.             if (string)
  845.             {
  846.                 strcpy (ptr, text + ActualBlock.start_column);
  847.                 ptr += strlen (ptr);
  848.  
  849.                 *ptr ++ = '\n';
  850.             }
  851.             else
  852.                 length += linelen - ActualBlock.start_column + 2;
  853.             }
  854.             else /* line is longer than block wide */
  855.             {
  856.             linelen = ActualBlock.end_column -
  857.                         ActualBlock.start_column + 1;
  858.  
  859.             if (string)
  860.             {
  861.                 strncpy (ptr, text + ActualBlock.start_column,
  862.                     linelen);
  863.                 ptr += linelen;
  864.  
  865.                 *ptr ++ = '\n';
  866.             }
  867.             else
  868.                 length += linelen;
  869.             }
  870.         }
  871.         break;
  872.         } /* switch */
  873.         }
  874.  
  875.         if (string && ptr-string > length)
  876.         {
  877.         D(bug("Aborting ... String exceeds "
  878.               "(%ld:%ld) )buffer in line %ld !\n",
  879.             ptr-string, length, line));
  880.         }
  881.  
  882.         if (string)
  883.         break;    /* done ! */
  884.         else
  885.         {
  886.         string = malloc (length+256);   /* allocate memory */
  887.         ptr = string;            /* points to EOS */
  888.         }
  889.  
  890.     /* after the loop, the string must be non-NULL, else we didn't get the
  891.        memory ! */
  892.     } while (string != NULL);
  893.  
  894.     if (string) /* add '\0' */
  895.     {
  896.         *ptr = 0;
  897.  
  898.         if (length != strlen (string)+1)
  899.         {
  900.         D(bug("Fehler: length: %ld  strlen: %ld\n",
  901.             length, strlen(string)+1));
  902.         }
  903.     }
  904.     }
  905.     else
  906.     string = NULL;
  907.  
  908.     return (string);
  909. } /* block_to_string */
  910.  
  911.  
  912. /*****************************************************************************
  913.  
  914.     NAME
  915.     do_bdelete
  916.  
  917.     PARAMETER
  918.     void
  919.  
  920.     RETURN
  921.     void
  922.  
  923.     DESCRIPTION
  924.     Deletes the marked block from the text.
  925.  
  926. ******************************************************************************/
  927.  
  928. void do_bdelete (void)
  929. {
  930.     long lines;
  931.     ED * bep     = ActualBlock.ep;
  932.     ED * saveed  = Ep;
  933.  
  934.     /* TODO */
  935.     if (ActualBlock.type != BT_LINE)
  936.     {
  937.     error ("bdelete:\nCannot use for this\ntype of block.");
  938.     return;
  939.     }
  940.  
  941.     /* is there a valid block ? */
  942.  
  943.     if (block_ok ())
  944.     {
  945.     /* don't allow it in VIEWMODE */
  946.  
  947.     if (bep->viewmode)
  948.     {
  949.         error ("%s:\nCannot delete from a\nwrite-protected text", av[0]);
  950.         return;
  951.     } /* if */
  952.  
  953.     /* change editor */
  954.  
  955.     switch_ed (bep);
  956.  
  957.     /* length of block */
  958.  
  959.     lines = ActualBlock.end_line - ActualBlock.start_line + 1;
  960.  
  961.     /* if line is inside the block, move to start of block. If it is
  962.        beyond the end-line, move up */
  963.  
  964.     if (bep->line >= ActualBlock.start_line && bep->line <= ActualBlock.end_line)
  965.         bep->line = ActualBlock.start_line;
  966.     else if (bep->line > ActualBlock.end_line)
  967.         bep->line -= lines;
  968.  
  969.     /* same for the 1st line */
  970.  
  971.     if (bep->topline >= ActualBlock.start_line && bep->topline <= ActualBlock.end_line)
  972.         bep->topline = ActualBlock.start_line;
  973.     else if (bep->topline > ActualBlock.end_line)
  974.         bep->topline -= lines;
  975.  
  976.     /* free memory and copy lines from beyond the block back */
  977.  
  978.     freelist (bep->list + ActualBlock.start_line, lines);
  979.     bmovl (bep->list + ActualBlock.end_line + 1,
  980.            bep->list + ActualBlock.start_line,
  981.            bep->lines - ActualBlock.end_line - 1);
  982.  
  983.     /* adjust number of lines. Text has been modified */
  984.  
  985.     bep->lines   -= lines;
  986.     bep->modified = 1;
  987.  
  988.     /* move cursor if it's now outside the text */
  989.  
  990.     if (bep->line >= bep->lines)
  991.         bep->line = bep->lines - 1;
  992.  
  993.     /* move topline if it's now outside the text */
  994.  
  995.     if (bep->topline >= bep->lines)
  996.         bep->topline = bep->lines - 1;
  997.  
  998.     /* no lines in text ? */
  999.  
  1000.     if (bep->line < 0)
  1001.         bep->topline = bep->line = 0;
  1002.  
  1003.     /* create at least one line */
  1004.  
  1005.     if (bep->lines == 0)
  1006.     {
  1007.         bep->lines ++;
  1008.         SETLINE(bep,0) = allocline(1);
  1009.     }
  1010.  
  1011.     /* get current line */
  1012.     text_load ();
  1013.  
  1014.     /* no block anymore */
  1015.     ActualBlock.type = BT_NONE;
  1016.  
  1017.     /* always redisplay */
  1018.     text_adjust (TRUE);
  1019.  
  1020.     /* adjust scroller */
  1021.     prop_adj ();
  1022.  
  1023.     /* old editor */
  1024.     switch_ed (saveed);
  1025.     }
  1026. } /* do_bdelete */
  1027.  
  1028.  
  1029. /*****************************************************************************
  1030.  
  1031.     NAME
  1032.     do_bcopy
  1033.  
  1034.     PARAMETER
  1035.     void
  1036.  
  1037.     RETURN
  1038.     void
  1039.  
  1040.     DESCRIPTION
  1041.     Copies a block to the line above the cursor. It is possible to
  1042.     copy the block into itself thus making it bigger.
  1043.  
  1044. ******************************************************************************/
  1045.  
  1046. void do_bcopy (void)
  1047. {
  1048.     LINE  * list;
  1049.     long    lines,
  1050.         i;
  1051.     ED      * ep = Ep;
  1052.     ubyte * str;
  1053.  
  1054.     /* update line */
  1055.  
  1056.     text_sync ();
  1057.  
  1058.     /* TODO */
  1059.     if (ActualBlock.type != BT_LINE)
  1060.     {
  1061.     error ("bcopy:\nCannot use for this\ntype of block.");
  1062.     return;
  1063.     }
  1064.  
  1065.     /* No valid block ? */
  1066.  
  1067.     if (!block_ok ())
  1068.     return;
  1069.  
  1070.     /* get length of block */
  1071.  
  1072.     lines = ActualBlock.end_line - ActualBlock.start_line + 1;
  1073.  
  1074.     /* make sure we have enough space */
  1075.  
  1076.     if (extend (ep, lines))
  1077.     {
  1078.     /* get memory for lines */
  1079.  
  1080.     if (list = (LINE *)alloclptr(lines))
  1081.     {
  1082.         /* copy lines to buffer */
  1083.  
  1084.         bmovl (ActualBlock.ep->list+ActualBlock.start_line, list, lines);
  1085.  
  1086.         /* make room for the lines */
  1087.  
  1088.         bmovl (ep->list+ep->line, ep->list+ep->line+lines,
  1089.             ep->lines-ep->line);
  1090.  
  1091.         /* now copy each separate line */
  1092.  
  1093.         for (i = 0; i < lines; i ++)
  1094.         {
  1095.         /* get memory for one line */
  1096.  
  1097.         str = allocline (LENGTH ((char *)list[i]) + 1);
  1098.  
  1099.         /* no more memory ? */
  1100.  
  1101.         if (!str)
  1102.         {
  1103.             /* set error */
  1104.  
  1105.             nomemory ();
  1106.  
  1107.             /* free memory */
  1108.  
  1109.             FreeMem (list, lines * sizeof(LINE));
  1110.             freelist (ep->list + ep->line, i);
  1111.  
  1112.             /* make space for block disappear */
  1113.  
  1114.             bmovl (ep->list+ep->line+lines, ep->list+ep->line,
  1115.                 ep->lines-ep->line);
  1116.  
  1117.             return;
  1118.         }
  1119.  
  1120.         /* copy contents */
  1121.  
  1122.         strcpy ((char *)str, (char *)list[i]);
  1123.  
  1124.         /* add line to list */
  1125.  
  1126.         GETTEXT(ep,ep->line+i) = str;
  1127.         }
  1128.  
  1129.         /* free buffer */
  1130.  
  1131.         FreeMem (list, lines * sizeof(LINE));
  1132.  
  1133.         /* if we inserted the block before the start of the block,
  1134.            we must adjust it */
  1135.  
  1136.         if (ep == ActualBlock.ep)
  1137.         {
  1138.         if (ep->line <= ActualBlock.start_line)
  1139.         {
  1140.             ActualBlock.start_line += lines;
  1141.             ActualBlock.end_line   += lines;
  1142.         } else if (ep->line <= ActualBlock.end_line)
  1143.             ActualBlock.end_line   += lines;
  1144.         }
  1145.  
  1146.         /* Text has been modified. Also the number of lines has changed. */
  1147.  
  1148.         ep->modified = 1;
  1149.         ep->lines += lines;
  1150.  
  1151. #ifdef NOTDEF
  1152.         /* update display */
  1153.         if (!text_adjust (FALSE))
  1154.         {
  1155.         scroll_display (0, -lines, ep->topcolumn, ep->line,
  1156.             ep->topcolumn+Columns, ep->topline+Lines);
  1157.         }
  1158. #endif
  1159.  
  1160.         /* Move cursor to the bottom */
  1161.  
  1162.         ep->line += lines;
  1163.  
  1164.         /* get new contents */
  1165.  
  1166.         text_load ();
  1167.  
  1168.         if (!text_adjust (FALSE)) /* display is adjusted, if needed */
  1169.         {
  1170.         scroll_display (0, -lines, ep->topcolumn, ep->line-lines,
  1171.             ep->topcolumn+Columns, ep->topline+Lines-1);
  1172.         }
  1173.  
  1174.         /* adjust scroller */
  1175.         prop_adj ();
  1176.     } else
  1177.         nomemory ();
  1178.     } else
  1179.     nomemory ();
  1180. } /* do_bcopy */
  1181.  
  1182.  
  1183. /*****************************************************************************
  1184.  
  1185.     NAME
  1186.     do_bmove
  1187.  
  1188.     PARAMETER
  1189.     void
  1190.  
  1191.     RETURN
  1192.     void
  1193.  
  1194.     DESCRIPTION
  1195.     Moves the marked block between the current line and the line
  1196.     above it. It's not possible to move the block into itself.
  1197.  
  1198. ******************************************************************************/
  1199.  
  1200. void do_bmove (void)
  1201. {
  1202.     long   lines;
  1203.     LINE * list;
  1204.     ED     * ep = Ep;
  1205.  
  1206.     /* update text */
  1207.  
  1208.     text_sync ();
  1209.  
  1210.     /* TODO */
  1211.     if (ActualBlock.type != BT_LINE)
  1212.     {
  1213.     error ("bmove:\nCannot use for this\ntype of block.");
  1214.     return;
  1215.     }
  1216.  
  1217.     /* no block ?? */
  1218.  
  1219.     if (!block_ok ())
  1220.     return;
  1221.  
  1222.     /* not allowed in viewmode */
  1223.  
  1224.     if (ActualBlock.ep->viewmode)
  1225.     {
  1226.     error ("%s:\nCannot move from a\nwrite-protected text", av[0]);
  1227.     return;
  1228.     } /* if */
  1229.  
  1230.     /* it is not very usefull to move a block into itself */
  1231.  
  1232.     if (    ActualBlock.ep == ep                &&
  1233.         ep->line >= ActualBlock.start_line    &&
  1234.         ep->line <= ActualBlock.end_line    )
  1235.     {
  1236.     error ("bmove:\nCannot move a\nblock into itself");
  1237.     return;
  1238.     }
  1239.  
  1240.     /* length of block */
  1241.  
  1242.     lines = ActualBlock.end_line - ActualBlock.start_line + 1;
  1243.  
  1244.     /* get memory for a copy of the block */
  1245.  
  1246.     if (!(list = (LINE *)alloclptr (lines)))
  1247.     {
  1248.     nomemory ();
  1249.     return;
  1250.     }
  1251.  
  1252.     /* text has been modified */
  1253.     ActualBlock.ep->modified = ep->modified = 1;
  1254.  
  1255.     /* copy block to tmp. memory */
  1256.     bmovl (ActualBlock.ep->list + ActualBlock.start_line, list, lines);
  1257.  
  1258.     /* are we in the editor that has the block or do we have to move the
  1259.        block to some other editor ? */
  1260.     if (ep == ActualBlock.ep)
  1261.     {
  1262.     /* behind the block: move the lines behind the block upto the current
  1263.        line up to the line where the block started and copy the former
  1264.        block into the new space */
  1265.  
  1266.     if (ep->line > ActualBlock.start_line)
  1267.     {
  1268.         bmovl ( ep->list + ActualBlock.end_line + 1,
  1269.             ep->list + ActualBlock.start_line,
  1270.             ep->line - ActualBlock.end_line -1);
  1271.         bmovl (list, ep->list + ep->line - lines, lines);
  1272.     } else
  1273.     {   /* before the block: copy all lines from the current upto
  1274.            the line where the block began up to the block-end
  1275.            and copy the former block into the new space */
  1276.  
  1277.         bmovl (ep->list + ep->line, ep->list + ep->line + lines,
  1278.             ActualBlock.start_line - ep->line);
  1279.         bmovl (list, ep->list + ep->line, lines);
  1280.  
  1281.         /* move down */
  1282.         ep->line += lines;
  1283.     }
  1284.  
  1285.     /* load (new) current line */
  1286.     text_load ();
  1287.     } else
  1288.     {    /* move block between editors */
  1289.  
  1290.     /* make room in the editor that gets the block */
  1291.     if (extend (ep, lines))
  1292.     {
  1293.         /* copy the lines after the block the beginning of the block
  1294.            thus deleting him */
  1295.  
  1296.         bmovl ( ActualBlock.ep->list + ActualBlock.end_line + 1,
  1297.             ActualBlock.ep->list + ActualBlock.start_line,
  1298.             ActualBlock.ep->lines - ActualBlock.end_line - 1);
  1299.  
  1300.         /* make room in the list of lines for the block */
  1301.         bmovl ( ep->list + ep->line,
  1302.             ep->list + ep->line + lines,
  1303.             ep->lines - ep->line);
  1304.  
  1305.         /* copy the buffer to its new place */
  1306.         bmovl (list, ep->list + ep->line, lines);
  1307.  
  1308.         /* adjust the number of lines both editors have now */
  1309.         ep->lines          += lines; /* he gets the block */
  1310.         ActualBlock.ep->lines -= lines; /* he lost it */
  1311.  
  1312.         /* the current line in the editor that had the block was inside
  1313.            the block */
  1314.         if (    ActualBlock.ep->line >= ActualBlock.start_line  &&
  1315.             ActualBlock.ep->line <= ActualBlock.end_line    )
  1316.         {
  1317.         /* move cursor to the place where the block started */
  1318.         ActualBlock.ep->line = ActualBlock.start_line;
  1319.  
  1320.         /* if the block was the last thing in this text, move up
  1321.            one more line */
  1322.         if (ActualBlock.ep->line == ActualBlock.ep->lines)
  1323.             ActualBlock.ep->line --;
  1324.         }
  1325.  
  1326.         /* if the line was behind the last line of the block, move up
  1327.            the length of the block in lines. */
  1328.         if (ActualBlock.ep->line > ActualBlock.end_line)
  1329.         ActualBlock.ep->line -= lines;
  1330.  
  1331.         /* ... but not too far */
  1332.         if (ActualBlock.ep->line < 0)
  1333.         ActualBlock.ep->line = 0;
  1334.  
  1335.         /* no block anymore */
  1336.         ActualBlock.type = BT_NONE;
  1337.  
  1338.         /* if we moved the whole text, make sure the old editor
  1339.            al least contains one single line */
  1340.         if (ActualBlock.ep->lines == 0)
  1341.         {
  1342.         LINE ptr = allocline (1);
  1343.  
  1344.         GETTEXT(ActualBlock.ep,0) = ptr;
  1345.         ActualBlock.ep->lines ++;
  1346.         }
  1347.  
  1348.         ep->line += lines;    /* Move cursor down */
  1349.  
  1350.         text_load ();       /* text_switch() calls text_sync() ! */
  1351.         switch_ed (ActualBlock.ep); /* make source to the active editor */
  1352.  
  1353.         if (!Ep->iconmode)
  1354.         {         /* Not iconified */
  1355.         text_adjust (TRUE);
  1356.  
  1357.         /* adjust scroller */
  1358.         prop_adj ();
  1359.         }
  1360.  
  1361.         switch_ed (ep);             /* old Ed */
  1362.     }
  1363.     }
  1364.  
  1365.     ActualBlock.type = BT_NONE;    /* We don't have any block anymore */
  1366.  
  1367.     /* free tmp. buffer */
  1368.     FreeMem (list, lines * sizeof(LINE));
  1369.  
  1370.     /* force redisplay */
  1371.     text_adjust (TRUE);
  1372. } /* do_bmove */
  1373.  
  1374.  
  1375. /*****************************************************************************
  1376.  
  1377.     NAME
  1378.     do_bsource
  1379.  
  1380.     PARAMETER
  1381.     void
  1382.  
  1383.     RETURN
  1384.     void
  1385.  
  1386.     DESCRIPTION
  1387.     Executes all lines inside a block as if they were typed as commands.
  1388.     The block is removed before the execution starts. The text
  1389.     is updated before every line. Thus self-modifying code is possible.
  1390.  
  1391.     NOTE
  1392.     - since the start and end lines are loaded immediately and the
  1393.       block unblock'd before execution starts, you can theoretically have
  1394.       another BSOURCE as part of this BSOURCE (but be carefull!).
  1395.     - BSOURCE allows self-modifying code
  1396.  
  1397. ******************************************************************************/
  1398.  
  1399. void do_bsource (void)
  1400. {
  1401.     ED * ep;
  1402.     char buf[MAXLINELEN];
  1403.     int  i, start, end;
  1404.  
  1405.     /* TODO */
  1406.     if (ActualBlock.type != BT_LINE)
  1407.     {
  1408.     error ("bsource:\nCannot use for this\ntype of block.");
  1409.     return;
  1410.     }
  1411.  
  1412.     if (block_ok ())
  1413.     {
  1414.     /* find start and end */
  1415.  
  1416.     ep    = ActualBlock.ep;
  1417.     start = ActualBlock.start_line;
  1418.     end   = ActualBlock.end_line + 1;
  1419.  
  1420.     /* unblock */
  1421.  
  1422.     text_redrawblock (0);
  1423.  
  1424.     /* execute every line */
  1425.  
  1426.     for (i = start; i < end; i ++)
  1427.     {
  1428.         text_sync (); /* make sure we are using the latest text */
  1429.  
  1430.         if (*(GETTEXT(ep,i)) != '#')
  1431.         {
  1432.         strcpy (buf, GETTEXT(ep,i));
  1433.  
  1434.         if (!do_command (buf))
  1435.             break;
  1436.         }
  1437.     }
  1438.     }
  1439. } /* do_bsource */
  1440.  
  1441.  
  1442. /******************************************************************************
  1443. *****  ENDE block.c
  1444. ******************************************************************************/
  1445.