#include<stdio.h>
 
/* 




                 VV                   VV    EEEEEEEEEEEEEE   
                  VV                 VV     EE  
                   VV               VV      EE
                    VV             VV       EE
                     VV           VV        EE
                      VV         VV         EEEEEEEEEEEEEE
                       VV       VV          EE
                        VV     VV           EE
                         VV   VV            EE
                          VV VV             EE
                           VVV              EE
                            V               EEEEEEEEEEEEEE
                                                       
 
 
 
 
                       MODULE  : testdos.c (blockcmd.c)
 
                       VERSION : v1.0
 
                       REVISED : 16 Sep 2004
 
                       AUTHOR  : Michael A. Campbell
 
 
 
 
 
 


                          FUNCTIONAL DESCRIPTION
                          ----------------------
 
               This module contains the block commands for the VE
 
               editor.  All subcommands of the BLOCK command are 
 
               implemented here.
 











*/

/* .page */

/*****************
 * INCLUDE FILES *
 *****************/

#include <string.h>
#include <curses.h>

#include "../def/common.def"
#include "../def/cmdix.def"


#include "../str/vestruct.str"


#include "../inc/msg.inc"
#include "../inc/vedbase.inc"
#include "../inc/termctl.inc"


#include "../ext/veutils.ext"
#include "../ext/termif.ext"
#include "../ext/lnklst.ext"
#include "../ext/strprim.ext"
#include "../ext/blockcmd.ext"

/* .page */

/*******************
 * LOCAL VARIABLES *
 *******************/

static char        key;            /* block subcommand selection    */
static int         length;         /* strings read in from console  */
static FILE        *block_conn;    /* files used in block writes    */

static char        start_char;     /* start and end text chars      */
static char        end_char;

static struct node *first, *last;  /* first and last nodes of block */
static struct node *curr;          /* curr node of block            */



/***************
 *  CONSTANTS  *
 ***************/


/* Commands, and the procedures to be activated by them */

char block_cmds [8] = { 'B', 'D', 'G', 'R', 'W', 'A', 'P', ESC };

void (*block_cmd_procs[7]) () = {
    &block_buffer,
    &block_delete,
    &block_get,
    &block_read,
    &block_write,
    &block_buffer,
    &block_purge    };

/* .page */

/********************
 * LOCAL PROCEDURES *
 ********************/

void block_purge ()

/*****************************************************************************
 *                                                                           *
 *   Delete anything in the current block, and clear its descriptor.         *
 *                                                                           *
 *****************************************************************************/

begin

    cnode = curr_block.head;

    while (cnode != EOL) begin

        nnode = cnode->fwd_link;
        free (cnode);

        cnode = nnode;
    end

    setb (0, &curr_block, sizeof(curr_block));

end /* block_purge */

/* .page */

void block_write ()

/*****************************************************************************
 *                                                                           *
 *   Write the contents of the current block out to a user specified file.   *
 *                                                                           *
 *****************************************************************************/

begin

    /* Prompt the user for the file to be written to */

    prompt_read_str ( 0, 0, prompt_msg[P_FILE], -1, -1, &temp.text );

    /* Attempt to open the file */

    block_conn = write_open ( &temp.text, &status );

    if ( status == E_OPEN_ERROR ) then
        ve_exception = E_OPEN_ERROR;

    /* File was opened OK : write the block out to it */

    else if ( status == E_OK ) begin

        move_cursor (0, 0);
        writestr (wde);

        writestr (&temp.text);

        cnode = block[0];            /* Pick up start and end locations      */
        pnode = block[1];

        while (cnode != pnode) begin
            if (options.output_tab_translation) then
                cnode->length = pack (&cnode->text, cnode->length,
                                      options.tab_stop);

            fwrite ( cnode->text, cnode->length, 1, block_conn );
            fputc ('\n', block_conn );

            cnode = cnode->fwd_link;
        end

        /* Clean up after the write */

        writestr (" has been written");
        fclose (block_conn);

        pause ();

        restore_cursor ();

    end /* Write out file */

end /* block_write */

/* .page */

void block_get ()

/*****************************************************************************
 *                                                                           *
 *   Get the contents of the current block, and insert them into the users   *
 *   file at curr_line.                                                      *
 *                                                                           *
 *****************************************************************************/

begin

    register struct node *last_line, *new_line;
    int lines_to_end_of_screen, lines_inserted, cy;

    if (curr_block.head == EOL) then  /* Nothing in block to get !           */
        return;

    lines_inserted = 0;
    lines_to_end_of_screen = lines - curr_screen.cy;

    cnode = curr_block.head;
    last_line = curr_line = curr_line->bwd_link;

    /* For the entire contents of the block buffer, create a copy of the     */
    /* line in the buffer, and add that copy to the current file.            */

    while (cnode != EOL) begin

        new_line = (struct node *) malloc (cnode->length + DES_SIZE);

        if (new_line == 0) begin       /* If no memory, then stop and exit    */
            curr_rep = requested_reps; /* Stop any repitition in progress     */
            set_status_line (TRUE, E_MEM, ve_mode);
            pause ();
            break;
        end

        movb (&cnode->length, &new_line->length, cnode->length + 2);

        add (&curr_file, last_line, new_line, &status);
        last_line = new_line;

        cy = curr_screen.cy + lines_inserted++;

        /* Update the screen display by inserting the new line onto the     */
        /* screen, if we have not filled up the available space between the */
        /* current line and the end of the screen.                          */

        if (lines_inserted <= lines_to_end_of_screen) begin
            move_cursor (0, cy);
            writestr (wil);
            move_cursor (0, cy);
            writeln (new_line->text, new_line->length, 0);
        end

        pnode = cnode;
        cnode = cnode->fwd_link;
        if (key == 'R') then
            free (pnode);
    end

    curr_line = curr_line->fwd_link;

    /* Update the curr_screen structure, and restore the cursor */

    cy = update_curr_screen (curr_screen.cy);
    restore_cursor ();

end /* block_get */

/* .page */

void block_buffer ()

/******************************************************************************
 *                                                                            *
 *  Copy the delimited block into the current block structure.  If this was   *
 *  called from the Block_Append function, simply add the delimited block to  *
 *  the end of the current one.  Else, purge the current block first.  If     *
 *  this is called from the Block_Delete function, simply link the delimited  *
 *  block onto the current block.  Else, copy each line to a new line which   *
 *  will be linked onto the current block.                                    *
 *                                                                            *
 ******************************************************************************/

begin

    ve_exception = E_LIST_ERROR;

    if (key != 'A') then    /* Not called from Block_Append function */
        block_purge ();     /* Purge the current block               */

    /* Pick up the start and end of the currently delimited block.   */

    curr  = first = block[0];
    last  = block[1];

    while (curr != last) begin

        nnode = curr->fwd_link;

        if (key == 'D') then    /* Called from Block_Delete function         */
            pnode = delete (&curr_file, curr, &status);

        else begin              /* Not called from Block_Delete              */

            pnode = (struct node *) malloc (curr->length + DES_SIZE);

            if (pnode == NULL) begin   /* Out of memory                         */
                block_purge ();
                set_status_line (TRUE, E_MEM, ve_mode);
                pause ();
                break;
            end

            movb (&curr->length, &pnode->length, curr->length + 2);
        end

        send_node (&curr_block, pnode);

        curr = nnode;

        ve_exception = E_OK;

    end /* while curr != last */

    restore_cursor ();

end /* block_buffer */

/* .page */

void block_delete ()

/******************************************************************************
 *                                                                            *
 *   Delete the currently delimited block from the users file, and place it   *
 *   into the current block                                                   *
 *                                                                            *
 ******************************************************************************/

begin

    int y, on_screen;

    /* Pick up the start of the screen area to begin updating at            */

    curr_line = block[0];
    curr_line = curr_line->bwd_link;
    on_screen = displayed (curr_line, &y);

    /* Move the delimited block from curr_file to curr_block                */

    block_buffer ();

    if (ve_exception == E_OK) begin   /* if move went OK                    */

        curr_screen.cx = 0;

        /* If the current line is displayed, update from there to the end of */
        /* the screen                                                        */

        if (curr_line == (struct node *) &curr_file) begin
            curr_line = curr_file.head;
            curr_screen.cy = update_curr_screen (2);
            draw_curr_screen ();
        end

        else if (on_screen) begin
            curr_screen.cy = y;

            curr_screen.cy = update_curr_screen (y);
            draw_to_endof_screen ();
        end

        /* If the current line is not displayed, update the screen data      */
        /* structures and draw a new screen                                  */

        else begin

            curr_screen.cy = update_curr_screen (11);

            draw_curr_screen ();
            restore_cursor ();
        end

        cursor_down_cmd ();

    end /* ve exception = E_OK */

end /* block delete */

/* .page */

void block_read ()

/******************************************************************************
 *                                                                            *
 *   Read a file into the current block.                                      *
 *                                                                            *
 ******************************************************************************/

begin

    int count;
    FILE *read_conn;

    block_purge ();        /* Clear out the current contents of the block    */

    /* Prompt for, and read in, the filename to read */

    length = prompt_read_str ( 0, 0, prompt_msg[P_FILE], -1, -1, &temp.text );
    read_conn = fopen ( temp.text, "r"); /* Attempt to open the file         */

    if (read_conn == NULL) then          /* Open attempt failed              */
        ve_exception = E_OPEN_ERROR;

    else begin                           /* Open succeeded: read the file    */

        while (fgets(temp.text, 256, read_conn) != NULL) begin

            count = strlen (temp.text);
            
            /* Added for DOS version, to ensure that VE lines do not have    */
            /* any line end characters. Internally, VE lines are simple      */
            /* character arrays, and are NOT null terminated. The ".length"  */
            /* field is all that determines their length.                    */
            
            temp.length = --count;  /* Delete the trailing '\n' on line      */

            /* If tabs are enabled and input tabs translation is enabled     */
            /* unpack the input line: expand tabs to spaces.                 */

            if (options.input_tab_translation) then
                temp.length = unpack (&temp.text, count, options.tab_stop);

            cnode = (struct node *) malloc (temp.length + DES_SIZE);

            /* If the file is so large that all available memory has been    */
            /* consumed, write out an error message.                         */

            if (cnode == NULL) begin
                set_status_line (TRUE, E_MEM, ve_mode);
                pause ();
                block_purge ();
                break;
            end

            /* Else copy the line into the node and add the node to the list */

            movb (&temp.length, &cnode->length, temp.length + 2);

            send_node (&curr_block, cnode);

        end  /* while */

        fclose (read_conn);   /* Close the current file */

    end /* Open succeeded */

    block_get ();          /* Insert the new block into the current file     */

    /* Block_get free's the lines as they are inserted into the current file */
    /* set the curr_block to reflect its empty status                        */

    setb (0, &curr_block, sizeof(curr_block));

end /* block_read */

/* .page */

void delete_markers ()

/******************************************************************************
 *                                                                            *
 *   Delete the visual delimiters of the current block, which have been       *
 *   output during this block session.                                        *
 *                                                                            *
 ******************************************************************************/

begin

    int y;

    /* Replace the first marker (start of block)                             */

    cnode = block_start;
    cnode->text[0] = start_char;    /* Char replaced by first marker         */

    /* Update the visual display as appropriate                              */

    if (displayed(block_start, &y)) begin
        move_cursor (0, y);
        writechar (start_char);
    end

    /* Replace the second marker, if required                                */

    if (block_start != block_end) begin
        curr_line->text[0] = end_char;    /* Char replaced by second marker  */

        restore_cursor ();
        writechar (end_char);
    end

    /* Return the cursor to its current x, y                                 */

    restore_cursor ();

end /* delete_markers */

/* .page */

/*********************
 * PUBLIC PROCEDURES *
 *********************/

void block_cmd ()

/*****************************************************************************
 *                                                                           *
 *  Implement the BLOCK command.  This includes seven subcommands            *
 *  (1) Block_Buffer    : Place area into block buffer                       *
 *  (2) Block_Delete    : Delete from file and place into block buffer       *
 *  (3) Block_Get       : Get from block buffer into file                    *
 *  (4) Block_Write     : Write delimited block to file                      *
 *  (5) Block_Append    : Append delimited area to block                     *
 *  (6) Block_Purge     : Purge the current block                            *
 *  (7) Block_Read      : Read a file into the current block                 *
 *                                                                           *
 *****************************************************************************/

begin

    int  cmd_ix, done, i;

    curr_screen.cx = 0;    /* Block only operates on lines                   */
    update_curr_line ();   /* Update current line before leaving it          */

    /* Initialize the controlling block variables                            */

    block[0] = block[1] = EOL;                  /* Block delimiters          */
    block_start = block_end = curr_line;        /* User start and end lines  */

    if (curr_line->length == 0) then            /* Insert blank start char   */
        curr_line->text[0] = ' ';

    start_char = end_char = curr_line->text[0]; /* Start and end text chars  */

    /* Present the user with a menu of available options                     */

    push_status_line (menu_msg[0]);             /* Will set cx = 0           */

    /* Output visual marker for start of block                               */

    writechar((curr_line->text[0] = '>'));
    move_cursor ( 0, curr_screen.cy );

    /* Allow the user to define the block by way of cursor_up, cursor_down,  */
    /* home, and jump commands                                               */

    FOREVER begin

        key = get_key ();

        cmd_ix = findb (commands, key, NUM_CMDS);

        /* If char is one of the cursor commands, home command, or jump cmd */
        /* execute that command, using resulting new curr_line as outer     */
        /* limit of the block.                                              */

        if ((cmd_ix >= cursor_right_ix && cmd_ix <= home_ix            ) ||
            (cmd_ix >= ext_pgup_ix     && cmd_ix <= ext_end_of_file_ix ) ||
            (cmd_ix == jump_ix)) begin

            /* If this is not the start of the block, replace the visual    */
            /* delimiter with the character it overwrote.                   */

            if (curr_line != block_start) begin
                restore_cursor ();
                writechar (end_char);
            end

            /* Execute the command that the user has entered                */

            (*cmd_proc[cmd_ix])();

            /* Put a visual terminator onto the new current line, and update */
            /* end of block indicator                                        */

            curr_screen.cx = 0;
            move_cursor ( 0, curr_screen.cy );
            writechar ( '>' );
            move_cursor ( 0, curr_screen.cy );
            
            block_end = curr_line;

            if (curr_line->length == 0) then
                curr_line->text[0] = ' ';

            end_char  = curr_line->text[0];

        end /* valid cursor command */

        /* Entered key is not a valid block definition command.  Exit block  */
        /* definition mode.                                                  */

        else begin

            key    = toupper(key);
            cmd_ix = findb ( &block_cmds, key, sizeof(block_cmds) );

            if ( cmd_ix == -1 ) then
                 writechar ( BEL );

            else begin
                delete_markers ();
                break;
            end

        end

    end /* FOREVER (input char valid for defining block) */

    /* The block is now defined, but because the user can go up or down in   */
    /* his definition, the block start may not be the lowest (closest to the */
    /* top) limit of the block.  Hence, go through the file to determine the */
    /* correct first and last positions of the block.                        */

    if (block_start != block_end) begin    /* a block has been defined       */

        cnode = curr_file.head;
        i = 0;

        while (cnode != EOL && i < 2) begin

            if (cnode == block_start || cnode == block_end) then
                block[i++] = cnode;

            cnode = cnode->fwd_link;

        end

    end /* The block is now defined */

/* .page */

    if ( key != ESC ) then
       (*block_cmd_procs[cmd_ix])();

    pop_status_line ();                   /* Restore the status line */

end /* block_cmd */

