#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  : columncmd.c
 
                       VERSION : v3.5
 
                       REVISED : 14 Jul 2005
 
                       AUTHOR  : Michael A. Campbell
 
 
 
 
 
 


                          FUNCTIONAL DESCRIPTION
                          ----------------------
 
               This module contains the VE column command and all of its
 
               subcommands. This command allows users to create, kill
 
               copy, paste, read and write columns (rectangular areas of text
               
               which may be one line long or span many lines) as opposed to lines. 
               
               The above actions for lines are handled by blockcmd.c. Both 

               modules (this one and blockcmd.c) allow users to exchange the

               the Block (line) and Column buffers, allowing line operations

               to be performed on Columns, and visa versa.




*/

/* .page */

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

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

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

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

#include "../inc/vedbase.inc"
#include "../inc/termif.inc"
#include "../inc/kbdif.inc"

#include "../ext/veutils.ext"
#include "../ext/termif.ext"
#include "../ext/kbdif.ext"
#include "../ext/lnklst.ext"
#include "../ext/strprim.ext"
#include "../ext/delete.ext"
#include "../ext/blockutil.ext"
#include "../ext/columncmd.ext"

/* .page */

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

static struct file_ctl column_block;  /* The current column buffer     */
static char        key;               /* column subcommand selection   */

static struct t_file_area last_file_area;
static char   last_key;

static char column_menu [] = "Insert  Kill  Delete  Copy  Paste  Read  SaveAs  Xchng  Erase : ";

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

/* Exit commands from the initial line range block definition */

char block_exit_cmds [8];

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

char column_cmds [9] = { 'I', 'K', 'D', 'C', 'P', 'R', 'S', 'X', 'E' };

void (*column_procs[9]) ( struct file_ctl *col_block, struct t_file_area *file_area ) = {
    &column_insert, 
    &column_copy_delete,
    &column_copy_delete,
    &column_copy_delete,
    &column_paste,
    &column_read,
    &column_write,
    &column_line_exchange,
    &column_flush   };

/* .page */

/**********************
 * PRIVATE PROCEDURES *
 **********************/

BOOL paste_dimensions_ok ( col_block, file_area )

/****************************************************************************
 *                                                                          *
 * This function checks the supplied column block against the supplied      *
 * file area to see if there are at least as many lines in the file         *
 * starting at the first line of the defined block as there are in the      *
 * col block. If this is so, column_paste allows the user to paste the      *
 * column block into the file starting at the first line of the defined     *
 * block without having to get the number of lines in the defined block     *
 * equal to the number of lines in the column block.                        *
 *                                                                          *
 * Simply put, this means that a user can define a column block, copy the   *
 * contents, and then move the cursor to the upper left edge of the desired *
 * insertion point. From there, the user can enter ESC-c, P to paste the    *
 * column block, starting at the cursor position. This saves having to      *
 * redefine the block for insertion to be exactly the same number of lines  *
 * as the original block when the copy was done, which is a very tedious    *
 * task.                                                                    *
 *                                                                          *
 * This function returns TRUE if there are at least as many lines in the    *
 * file starting from the first line of the defined block as there are in   *
 * column block, or FALSE if this is not the case.                          *
 *                                                                          *
 ****************************************************************************/

struct file_ctl    *col_block;       /* The column block to work with       */
struct t_file_area *file_area;       /* The area definition in the file     */

begin
   BOOL ret_value  = FALSE;          /* Result of this function             */
   int  file_lines = 0;              /* Number of lines from first in block */
   struct node *this_line;           /* Current line                        */

   /* Start by checking to see if the user has actually specified a line    */
   /* range. If this is the case, then only allow the paste if the size of  */
   /* the specified range is the same number of lines as the column block.  */

   if ( file_area->num_lines > 1 )
      return ( (file_area->num_lines == col_block->nodes) ? TRUE : FALSE );

   /* Else, carry on with determining if there are enough lines left in the */
   /* file to support pasting of the number of lines in the column block    */

   this_line = file_area->first_line;
   this_line = this_line->bwd_link;

   do
   begin
      this_line  = this_line->fwd_link;

      if ( this_line != NULL )
         file_lines += 1;
   end
   while ( this_line != NULL && file_lines < col_block->nodes );

   if ( file_lines == col_block->nodes )
      ret_value = TRUE;

   return ( ret_value );

end /* paste_dimensions_ok */

/* .page */

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

struct file_ctl *get_column_block_ptr ()

/****************************************************************************
 * This function returns a pointer to the column block. This pointer is     *
 * used by the status_cmd to get data usage size information regarding the  *
 * column block.                                                            *
 ****************************************************************************/

begin

   return ( &column_block );

end /* get_column_block_ptr */

/* .page */

void column_insert ( col_block, file_area )

/****************************************************************************
 *                                                                          *
 * This function  inserts one or more columns to the left of the cursor. If *
 * the  cursor is  at  column zero, it inserts at column zero, shifting the *
 * display  to to the right. This function accomplishes this by setting up  *
 * a column  block that contains the number of columns and the number of    *
 * rows specified by the user when they defined the file area, and then     *
 * calls column_paste to do the actual  work. Because  this  function uses  *
 * its own column block, it does not effect the global column block         *
 *                                                                          *
 ****************************************************************************/

struct file_ctl    *col_block;       /* The column block to work with       */
struct t_file_area *file_area;       /* The region definition in the file   */

begin
   struct   file_ctl insert_block;      /* Local column block               */
   struct   node     *this_line;        /* Working line of insert block     */
   register int      i;                 /* Loop counter                     */
   register int      num_lines;         /* Lines to affect                  */
   register int      columns_to_insert; /* Columns to insert left of cursor */

   init_list ( &insert_block );      /* Initialize local column block       */

   num_lines         = file_area->num_lines;
   columns_to_insert = file_area->last_column - file_area->first_column + 1;
   
   /* Create a column block with as many lines as the user has indicated,   */
   /* filling each line with blanks. Add one extra byte to each line to     */
   /* allow column_paste to add a trailing zero, making it a string         */

   for ( i = 0; i < num_lines; i++ )
   begin
      this_line = (struct node *) ve_alloc ( DES_SIZE + columns_to_insert + 1 );

      this_line->length = columns_to_insert;
      setb ( ' ', this_line->text, columns_to_insert );

      send_node ( &insert_block, this_line );
   end

   /* Now paste that column block in */

   column_paste ( &insert_block, file_area );

   column_flush ( &insert_block, file_area );

end /* column_insert */

/* .page */

void column_copy_delete ( col_block, file_area )

/**********************************************************************************
 *                                                                                *
 * This function implements the Copy, Kill and Delete column commands. Due to the *
 * great similarity in implementation, all three are grouped together. If called  *
 * as the Delete or Kill commands, this function deletes the contents of the user *
 * indicated column block. If called from Copy or Delete, it saves the indicated  *
 * area to the column block before deleting it.                                   *
 *                                                                                *
 **********************************************************************************/

struct file_ctl    *col_block;       /* The column block to work with             */
struct t_file_area *file_area;       /* The area definition in the file           */

begin
   struct node *this_line, *col_line; /* Current block line and copy line         */
   char resp [42];                    /* Success message buffer                   */
   register int cx1, cx2, cy;         /* Starting and ending columns and line     */
   register int i;                    /* Loop counter                             */
   register int lines_done = 0;       /* Num lines processed                      */
   register int first_line_num;       /* Line num of first line of range          */

   first_line_num = get_curr_line_num ();

   cx1 = file_area->first_column;
   cx2 = file_area->last_column;
   cy  = curr_screen.cy;

   /* Start by flushing the column block if this is a Copy or Delete command     */

   if ( key == 'C' || key == 'D' )
      column_flush ( col_block, file_area );

   /* Queue an undo action if we haven't been called from an undo action    */
   /* if this was a Delete or Kill command.                                 */

   if ( undo_state != US_undoing && file_area->parm1 != 'u' )
      if ( key == 'D' || key == 'K' )
         push_undo_column_delete ( column_ix, file_area, first_line_num, cx1, cy );

   this_line = file_area->first_line;
   this_line = this_line->bwd_link;

   do
   begin
      /* Advance to the next line in the block                              */

      this_line = this_line->fwd_link;

      if ( key == 'C' || key == 'D' || file_area->parm1 == 'u' )
      begin
         /* If this is Copy or Delete, we need to copy the column range     */
         /* to the column block before doing any possible deletion.         */
         /* Allocate a new line to hold the copied contents from the        */
         /* indicated columns. The "+1" is to allow column_paste to add a   */
         /* trailing zero to make it a string                               */

         col_line  = (struct node *) ve_alloc ( DES_SIZE + (cx2-cx1) + 1 );

         if ( this_line->length <= cx1 )
            /* If col marker is beyond end of line there is nothing to copy */

            col_line->length = 0;

         else
         begin
            /* Else there is something to copy - do so */

            if ( this_line->length > cx2 )
               col_line->length = cx2 - cx1 + 1;
            else
               col_line->length = this_line->length - cx1;

            for ( i = 0; i < col_line->length; i++ )
               col_line->text[i] = this_line->text[cx1 + i];
         end

         /* Add the copied column contents to the column block */

         send_node ( col_block, col_line );

      end

      /* If called from Delete or Kill, now delete the indicated            */
      /* column range from the line. The delete_chars function takes care   */
      /* of the deletion and the screen updating.                           */

      if ( (key == 'D' || key == 'K') && (file_area->parm1 != 'u') )
         delete_chars ( this_line, cx1, cx2, cy++ );

      lines_done   += 1;

   end
   while ( this_line != file_area->last_line );

   if ( file_area->parm1 != 'u' )
   begin
      /* If we are not being called by push_undo_column_delete to make a    */
      /* silent copy of the file area to be deleted, output a success msg   */

      sprintf ( resp, "%d %s %d %s", cx2-cx1+1, "columns x", lines_done, "lines" );
      if ( key == 'C' || key == 'D' )
         strcat ( resp, " copied" );
      if ( key == 'D' )
         strcat ( resp, "," );
      if ( key == 'D' || key == 'K' )
      strcat ( resp, " deleted" );

      write_ve_msg ( resp );
   end

end /* column_copy_delete */

/* .page */

void column_paste ( col_block, file_area )

/**********************************************************************************
 *                                                                                *
 * This function pastes the supplied col_block into the lines defined by the      *
 * supplied file area.                                                            *
 *                                                                                *
 **********************************************************************************/

struct file_ctl    *col_block;       /* The column block to work with             */
struct t_file_area *file_area;       /* The area definition in the file           */

begin
   BOOL update_screen = TRUE;        /* TRUE until last line of screen updated    */
   struct node *orig_curr;           /* curr_line at start                        */
   struct node *col_line;            /* curr line being processed in column block */
   register int cx, cy;              /* cx, cy at start                           */
   register int cols_done = 0;       /* Maximum number of columns pasted          */
   register int lines_done = 0;      /* lines processed                           */
   char resp [42];                   /* Success message buffer                    */

   if ( col_block->nodes == 0 )
   begin
      write_ve_msg ( "Column buffer empty - Nothing to paste" );
      return;
   end

   /* Next, a little sanity check - make sure that either the defined block we    */
   /* are pasting into is the same number of lines as the column block, or that   */
   /* if no line range has been defined, there are enough lines between start of  */
   /* defined block and end file to support the paste. If not, err out            */

   if ( !paste_dimensions_ok(col_block, file_area) )
   begin
      write_ve_msg ( "Column and area line sizes differ" );
      return;
   end

   orig_curr = curr_line->bwd_link;
   cx        = curr_screen.cx;
   cy        = curr_screen.cy;

   if ( undo_state != US_undoing )
      push_undo_column_paste ( column_ix, col_block, get_curr_line_num(), cx, cy );

   curr_line = file_area->first_line;
   curr_line = curr_line->bwd_link;
   col_line  = (struct node *) col_block;

   do
   begin
      curr_line  = curr_line->fwd_link;
      col_line   = col_line->fwd_link;
      lines_done += 1;
      cols_done  = (col_line->length > cols_done) ? col_line->length : cols_done;
      load_line_buf ();

      /* Check for this making the line too long - if so, disallow             */

      if ( curr_line->length + col_line->length > MAX_LINE_SIZE - 2 )
      begin
         write_ve_msg ( "1+ pastes skipped - line too long" );
         break;
      end

      else
      begin
         /* Do the pasting if the line to be pasted is non blank, OR if this is   */
         /* the Insert command, and the current line is longer than the present   */
         /* cursor position.                                                      */

         if ( ( col_line->length > 0  && key != 'I' ) ||
              ( line_buf.length  > cx && key == 'I' ) )
         begin
            /* Check to see if line is long enough to do this. If not, expand it. */
            /* This relies on the fact that load_line_buf pads the line buffer    */
            /* with spaces.                                                       */

            line_buf.length = (line_buf.length < cx) ? cx : line_buf.length;

            /* Do the paste into the current line by copying from the current line   */
            /* up to the cursor position, and then copying the information for this  */
            /* line from the column buffer, and then finally copying from the cursor */
            /* position onwards from the current line.                               */

            movb ( line_buf.text,      temp.text,                       cx                   );
            movb ( col_line->text,     &temp.text[cx],                  col_line->length     );
            movb ( &line_buf.text[cx], &temp.text[cx+col_line->length], line_buf.length - cx );

            /* Update the newly pasted line back into the file. */

            temp.length = line_buf.length + col_line->length;
            movb ( &temp.length, &line_buf.length, temp.length + sizeof(int) );

         end /* do the insertion */

         /* Now update the screen display - note that the current column line */
         /* is always long enough to allow the addition of the trailing zero  */
         /* All column line allocators in this module add one to the          */
         /* required size for this purpose.                                   */

         if ( update_screen )
         begin
            col_line->text[col_line->length] = '\0';
            ti_insert_str ( col_line->text );

            if ( curr_line == curr_screen.bottom )
               update_screen = FALSE;
            else
               ti_move_cursor ( cx, ++curr_screen.cy );
         end

      end /* line will not be too long */
      
      update_curr_line ();

   end
   while ( col_line->fwd_link != NULL );

   /* The current line at this point should be the last line of the region  */
   /* definition. Since that line was likely replaced during the paste,     */
   /* update it here so that the block definition is re-usable if we are    */
   /* called again by the VE Again command.                                 */

   file_area->last_line = curr_line;
   file_area->num_lines = lines_done;

   /* Restore the current line and the cursor and we are done. The current  */
   /* line should also be the first line of the file area. Since it was     */
   /* likely replaced during the paste, update it at this point as well, so */
   /* that the file area definition will be valid if we are called again    */
   /* by the VE Again command.                                              */

   curr_line = file_area->first_line = orig_curr->fwd_link;
   ti_move_cursor ( (curr_screen.cx = cx), (curr_screen.cy = cy) );

   sprintf ( resp, "%d %s %d %s", cols_done, "columns x", lines_done, "lines pasted" );
   write_ve_msg ( resp );

end /* column_paste */

/* .page */

void column_read ( col_block, file_area )

/****************************************************************************
 *                                                                          *
 * This function reads a file into the current column block and then does a *
 * paste of the contents into the current file.                             *
 *                                                                          *
 ****************************************************************************/

struct file_ctl    *col_block;      /* The column block to work with        */
struct t_file_area *file_area;      /* The area definition in the file      */

begin
    register int length;                 /* Don't care                      */
    int num_chars, num_lines, checksum;  /* Information on read file        */
    char ff;                             /* File format                     */

    /* Prompt for the filename to read, attempt to read it.                 */

    open_file_dialog ( select_file_read, temp.text, MAX_LINE_SIZE, TRUE );

    if ( strlen(temp.text) != 0 ) 
    begin
       column_flush ( col_block, file_area );

       ve_exception = read_file ( temp.text, col_block, &num_chars, &num_lines,
                                             &checksum, &ff );
       if (ve_exception != E_OK ) 
       begin
          column_flush ( col_block, file_area );
          write_ve_msg ( err_msg[E_OPEN_ERROR] );
       end
       else
          column_paste ( col_block, file_area );

    end
    else
       write_ve_msg ( no_file_select_read );

end /* column_read */

/* .page */

void column_write ( col_block, file_area )

/**********************************************************************************
 *                                                                                *
 * This function writes out either the current column buffer or the currently     *
 * defined column buffer, depending on whether the user has defined a column area *
 * before issuing the column write command. This function prompts for a file name *
 * and then writes the column buffer or defined column area to that file.         *
 *                                                                                *
 **********************************************************************************/

struct file_ctl    *col_block;       /* The column block to work with        */
struct t_file_area *file_area;       /* The area definition in the file      */

begin
    int num_chars;         /* Number of chars written                        */
    int num_lines;         /* Number of lines written                        */
    int checksum;          /* Checksum of written block                      */
    struct node *cnode;    /* Current line                                   */
    struct node *pnode;    /* Last line                                      */
    char display_fn [42];  /* size limited version of file name              */
    struct file_ctl write_cb;         /* Local column block for writes       */

    if ( ( file_area->first_line   == file_area->last_line   ) &&
         ( file_area->first_column == file_area->last_column ) &&
         ( col_block->head            != EOL                       ) ) 
    begin
       /* User wishes to write out the contents of the column block (from    */
       /* a previous copy/delete operation)                                  */

       cnode = col_block->head;
       pnode = col_block->tail;
    end

    else begin
       /* User wishes to write out the currently defined block. Extract the  */
       /* current column area to a temporary write-specific column block,    */
       /* and then use that block as the target to write from. "key" is set  */
       /* to 'C' so that column_copy_delete knows it should copy, not delete */

       key = 'C';
       init_list ( &write_cb );
       column_copy_delete ( &write_cb, file_area );

       cnode = write_cb.head;
       pnode = write_cb.tail;
    end

    open_file_dialog ( select_file_write, filename_buf.text, MAX_LINE_SIZE, TRUE );
    
    if ( strlen(filename_buf.text) != 0 )
    begin
       ve_exception = write_file ( filename_buf.text, FALSE, 'u', cnode, pnode, 
                                   &num_chars, &num_lines, &checksum );

       truncate_str_left ( filename_buf.text, display_fn, 16 );
       if ( ve_exception == E_OK ) then
          strcat ( display_fn, " has been written" );
       else
          strcat ( display_fn, " failed to write" );
       write_ve_msg ( display_fn );

       ve_exception = E_OK;
    end
    else
       write_ve_msg ( no_file_select_write );

    if ( key == 'C' )
    begin
       /* If we created a temporary write-specific column block, free all   */
       /* the associated memory at this time and set the cmd letter back    */
       /* to its original state.                                            */

       column_flush ( &write_cb, file_area );
       key = 'S';
    end

end /* column_write */

/* .page */

void column_line_exchange ( col_block, file_area )

/*****************************************************************************
 *                                                                           *
 * This function exchanges the contents of the Column and Block (line)       *
 * buffers, allowing line operations to be carried out on column areas and   *
 * visa versa.                                                               *
 *                                                                           *
 *****************************************************************************/

struct file_ctl    *col_block;    /* The column block to work with           */
struct t_file_area *file_area;    /* The area definition in the file         */

begin
   exchange_line_column_blocks ();

   write_ve_msg ( "Column and Line buffers exchanged" );

end /* column_line_exchange */

/* .page */

void column_flush ( col_block, file_area )

/*****************************************************************************
 *                                                                           *
 * This function deletes everything in the column block, and clear its       *
 * descriptor.                                                               *
 *                                                                           *
 *****************************************************************************/

struct file_ctl    *col_block;    /* The column block to work with           */
struct t_file_area *file_area;    /* The area definition in the file         */

begin
   struct node *this_node, *next_node;

   purge_list ( col_block );

   /* If called from the Empty subcommand, output a success message          */

   if ( key == 'E' ) then
      write_ve_msg ( "Column Area buffer emptied" );

end /* column_flush */

/* .page */

void init_column_block ()

/*****************************************************************************
 *                                                                           *
 * This function is called when VE is intialized to set the column block to  *
 * a known initial state, and initialize column command variables.           *
 *                                                                           *
 *****************************************************************************/

begin
   init_list ( &column_block );

   /* Cursor left or cursor right will exit the line range definition and move to */
   /* column range definition                                                     */

   block_exit_cmds[0] = commands[cursor_left_ix ];
   block_exit_cmds[1] = commands[cursor_right_ix];

   /* The Insert, Paste, Read, Save or Empty commands (I, P, R, S or E) will exit */
   /* line range definition and skip column range definition, moving directly to  */
   /* execution of the command. For the I command, one column will be inserted    */

   block_exit_cmds[2] = 'I';
   block_exit_cmds[3] = 'P';
   block_exit_cmds[4] = 'R';
   block_exit_cmds[5] = 'S';
   block_exit_cmds[6] = 'X';
   block_exit_cmds[7] = 'E';

end /* init_column_block */

/* .page */

void column_cmd ()

/*****************************************************************************
 *                                                                           *
 *  Implement the Column command.  This includes seven subcommands:          *
 *  (1) Column_Insert   : Create column(s) to the left of the cursor         *
 *  (2) Column_Kill     : Delete column(s)                                   *
 *  (3) Column_Copy     : Copy column(s) to the column buffer                *
 *  (4) Column_Delete   : Kill column(s) but save a copy to the column buf   *
 *  (5) Column_Paste    : Paste the column_buffer to right of the cursor     *
 *  (6) Column_Write    : Write the defined column area to a file            *
 *  (7) Column_Flush    : Free the contents of the column buffer             *
 *                                                                           *
 *****************************************************************************/

begin
    register int cmd_ix;                /* Index of block command            */
    register int i;                     /* Loop counter                      */
    BOOL   range_defined;               /* Did user define a range?          */
    struct t_file_area file_area;       /* File area (rectangle) definition  */

    update_curr_line ();   /* Update current line before leaving it          */

    if ( curr_cmd == commands[again_ix] )
    begin
       /* We are being run via the VE Again command. Restore the last file   */
       /* area and the last command selection and re-execute                 */

       movb ( &last_file_area, &file_area, sizeof(file_area) );
       file_area.cmd = last_key;

       range_defined = TRUE;
    end

    else
    begin
       /* Else we will interact with the user to establish a file area and    */
       /* select a command to execute. Start by initializing the file area    */

       file_area.first_line   = file_area.last_line   = NULL;
       file_area.first_column = file_area.last_column = 0;
       file_area.parm1        = file_area.parm2       = '\0';
       file_area.parm3        = file_area.parm4       = 0;

       push_status_line ( column_menu );

       /* Interact with the user to define a column range. If they don't define */
       /* range  just exit. A column range consists of an initial block range,  */
       /* defining the lines of interest, and then a column range, defining the */
       /* columns of interest. The result defines a rectangular space (file     */
       /* area) for the column commands to work on.                             */

       range_defined = define_line_range ( &file_area, curr_screen.cx, block_exit_cmds, 8 );
       
       /* After the line range is successfully defined, check to see if the     */
       /* user entered the Insert, Paste, SaveAs or Empty commands. If so, skip */
       /* the column range definition and go right to the command execution.    */

       key = file_area.cmd;

       if ( ( range_defined                                                       ) && 
            ( key == commands[cursor_left_ix ] || key == commands[cursor_right_ix]) )
          range_defined = define_column_range ( &file_area, column_cmds, sizeof(column_cmds) );

    end

    if ( range_defined )
    begin
       /* If we are doing an Again of a column paste, redefine the current  */
       /* cursor position as the first line of the file area, so that the   */
       /* the paste occurs in the current spot, not the original spot.      */

       if ( curr_cmd == commands[again_ix] && file_area.cmd == 'P' )
          file_area.first_line = curr_line;

       /* Put the cursor back where it belongs - file_area definition has   */
       /* moved it.                                                         */

       curr_line = file_area.first_line;
       display_new_screen ( curr_screen.cx );

       /* Execute the requested column command, repeating as many times as  */
       /* has been requested by the user                                    */

       key    = file_area.cmd;
       cmd_ix = findb ( column_cmds, key, sizeof(column_cmds) );

       for ( i = 0; i < requested_reps; i++ )
          (*column_procs[cmd_ix])( &column_block, &file_area );

       requested_reps = 1;  /* Stops cmd_processor from iterating further */

    end

    /* Save off the block area definition and command key for possible future */
    /* use via an Again command                                               */

    movb ( &file_area, &last_file_area, sizeof(last_file_area) );
    last_key = key;

    if ( curr_cmd != commands[again_ix] )
       pop_status_line ();

end /* column_cmd */

