#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  : testunix.c (editcmd.c)

                       VERSION : v1.0

                       REVISED : 18 Sep 2004
  
                       AUTHOR  : Michael A. Campbell
 

 
 
 
 


                          FUNCTIONAL DESCRIPTION
                          ----------------------
 
              This module contains the edit command for the VE editor.
 
               This command initiates the editing of a new file. 
 






*/

/* .page */

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

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

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

#define BEL 0x07

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


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


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


/* .page */

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

void save_context ( file_ix )

/****************************************************************************
 *                                                                          *
 *   Save the current context of VE in the file descriptor of the file      *
 *   whose index is given.                                                  *
 *                                                                          *
 ****************************************************************************/

 int file_ix;         /* Index of descriptor to save current context in     */

 begin

    int really_stupid;              /*  Actual number of nodes moved over   */  /* blah */
    struct file_des_str *save_des;  /*  Descriptor being saved into         */

    if ( file_ix == -1 ) then
        return;

    else begin

        update_curr_line ();

        /* Move to the descriptor, and then copy current data to it.        */

        save_des = (struct file_des_str *) move_node ( &open_file_list, 
                                                       open_file_list.fwd_link, 
                                                       '+', 
                                                       &stupid,
                                                       file_ix,
                                                       &status );

        movb ( &curr_file,     &save_des->file_list,   sizeof(curr_file) );
        movb ( &curr_screen,   &save_des->screen_data, sizeof(curr_screen) );
        movb ( &curr_filename, &save_des->filename,    sizeof(curr_filename) );
        
        

        save_des->curr_line = curr_line;
        save_des->conn = curr_conn;

    end /* good file index */

end /* save_context */

/* .page */

void restore_context ( file_ix )

/****************************************************************************
 *                                                                          *
 *   Restore the context of VE from the file descriptor of the file         *
 *   whose index is given.                                                  *
 *                                                                          *
 ****************************************************************************/

 int file_ix;       /* Index of descriptor to restore context from          */

 begin

    int stupid;                     /* Actual number of nodes moved over    */
    struct file_des_str *rest_des;  /* Descriptor being restored from       */


    if ( file_ix == -1 ) then
        return;

    else begin

        /* Move to the descriptor, and then copy current data from it.       */

        rest_des = (struct file_des_str *) move_node ( &open_file_list, 
                                                       open_file_list.fwd_link, 
                                                       '+',
                                                       &stupid,         
                                                       file_ix,       
                                                       &status );

        movb ( &rest_des->file_list,   &curr_file,      sizeof(curr_file) );
        movb ( &rest_des->screen_data, &curr_screen,    sizeof(curr_screen) );
        movb ( &rest_des->filename,    &curr_filename,  sizeof(curr_filename) );

        curr_line = rest_des->curr_line;
        curr_conn = rest_des->conn;

        curr_file_des = rest_des;

    end /* good file index */

end /* restore_context */

/* .page */

void context_switch ( old_ix, new_ix )

/****************************************************************************
 *                                                                          *
 *  Switch between the current file, whose index is old_ix, and the new     *
 *  file, whose index is new_ix.  This involves saving the old context      *
 *  and restoring a new one.                                                *
 *                                                                          *
 ****************************************************************************/

 int old_ix;        /* Indices of old and new file descriptors              */
 int new_ix;

 begin

    save_context    ( old_ix );
    restore_context ( new_ix );

    curr_file_ix = new_ix;

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

    restore_cursor ();

end /* context_switch */

/* .page */

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

int get_file ( file_name )

/*****************************************************************************
 *                                                                           *
 *  Search the current file list for a file  whose name is the same as the   *
 *  file name supplied.                                                      *
 *                                                                           *
 *****************************************************************************/

 char file_name [];

 begin

    struct file_des_str *des;
    int len, name_len, file_ix;

    des = (struct file_des_str *) open_file_list.fwd_link;
    name_len = strlen ( file_name );

    file_ix = 0;

    while ( des != EOL ) begin

        len = strlen ( des->filename );

        if ( len == name_len ) begin

            if ( strcmp ( file_name, des->filename ) == 0 ) then
                 return ( file_ix );
        end

        des = (struct file_des_str *) des->fwd_link;
        file_ix += 1;

    end

    return ( -1 );

end /* get_file */

/* .page */

void edit_file ( file_ix )

/****************************************************************************
 *                                                                          *
 *   Begin editing of the file whose index is supplied.  This file becomes  *
 *   the current file, and so this procedure sets up all of the relevant    *
 *   "current" data structures.                                             *
 *                                                                          *
 ****************************************************************************/

int file_ix;        /* Index of file descriptor for the file to edit        */

begin

    int actual, count, fbytes, flines;
    struct file_des_str *file_des;

    file_des = (struct file_des_str *) move_node ( &open_file_list, 
          open_file_list.fwd_link,  
          '+',
          &actual,         
          file_ix,       
          &status );

    line_buf.length = fbytes = flines = 0;

    /*  This file will become the current file, and so, set up the new  */
    /*  current attributes.                                             */

    setb ( 0, &curr_file, sizeof(curr_file) );
    setb ( 0, &curr_screen, sizeof(curr_screen) );
    strcpy ( curr_filename, file_des->filename );
    curr_conn = file_des->conn;

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

        count = strlen (temp.text);   /* Count doesn't include the \0    */
        
        /* 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'          */
        ve_exception = E_OK;

        /* 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);

        for ( actual = 0; actual < temp.length; actual++ ) begin

            /* Check for non text characters.  If any are found, reject */
            /* this file.                                               */

            if ( !is_char(temp.text[actual]) ) begin
                 ve_exception = E_BAD_FILE;
                 break;
            end
        end

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

        if ( cnode == NULL ) then
             ve_exception = E_MEM;

        /* If the file is bad, or large enough that all memory has been  */
        /* consumed, write out an error message and exit.                */

        if ( ve_exception != E_OK ) begin

            set_status_line (TRUE, ve_exception, last_ve_mode);
            discard_curr_file ();
            pause ();

            if ( files_open > 0 ) begin
                 context_switch ( -1, file_menu_select() );
                 return;
            end

            else
                 quit_ve ( ve_exception );

        end

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

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

        send_node (&curr_file, cnode);

        fbytes = fbytes + temp.length + 1;
        flines += 1;

    end  /* while */

    curr_screen.top    = curr_line = curr_file.head;
    curr_screen.bottom = move_node (&curr_file, curr_screen.top, (int)'+',
                                    &actual, lines - 3, &status);

    draw_curr_screen ();

    ve_msg = (char *) &temp.text;
    sprintf ( temp.text, "%u %s %d %s", fbytes, "Bytes",
               flines, "Lines" );

    curr_screen.cx = 0;
    curr_screen.cy = 2;

    curr_screen.direction = rcd;

    restore_cursor ();

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

end /* edit_file */

/* .page */

void edit_new_file ()

/****************************************************************************
 *                                                                          *
 *   Set up for the editing of a new, unnamed file.                         *
 *                                                                          *
 ****************************************************************************/

 begin

    /*  This file will become the current file, and so, set up the new  */
    /*  current attributes.                                             */

    setb ( 0, &curr_file, sizeof(curr_file) );
    setb ( 0, &curr_screen, sizeof(curr_screen) );

    strcpy ( curr_filename, curr_file_des->filename);
    curr_conn = NULL;

    /* Set up a default first line so that space to write is allocated. */

    curr_line = (struct node *) malloc (columns + DES_SIZE);
    check_mem_exception (curr_line);

    curr_line->length = 0;

    send_node (&curr_file, curr_line);

    curr_screen.top       = curr_line;
    curr_screen.bottom    = curr_line;
    curr_screen.direction = rcl;

    curr_screen.cx = 0;
    curr_screen.cy = 2;
    restore_cursor ();

    writestr (wclr_e);

end /* edit_new_file */

/* .page */

void add_file ( pathname )

/****************************************************************************
 *                                                                          *
 *   Add the file whose name is given to the set of active files currently  *
 *   being serviced by VE.  This file becomes the current file.             *
 *                                                                          *
 *   Adding the file involves allocating and initializing a new file        *
 *   descriptor, and then intiating the editing of the file, by calling an  *
 *   appropriate procedure.  If the supplied name is null (0), then a new   *
 *   empty file is added.  If the filename is non null, a new file is read  *
 *   in and set up;                                                         *
 *                                                                          *
 ****************************************************************************/

 char *pathname;

 begin

    int i;                              /* General purpose index            */
    struct file_des_str dummy_file_des; /* Dummy, for getting sizeof        */
    FILE *temp_conn;

    ve_exception = E_OK;
    temp_conn    = NULL;

    /*  If a new empty file is not being added, attempt to open the given   */
    /*  file to be sure that it exists.                                     */

    if ( pathname != NULL ) begin
        temp_conn = fopen ( pathname, "r" );
        if ( temp_conn == NULL ) then
            ve_exception = E_OPEN_ERROR;
    end

    if ( ve_exception == E_OK ) begin

        /*  Managed to open the file OK.  Allocate and initialize a file    */
        /*  descriptor block for it.                                        */

        file_des = (struct file_des_str *) malloc ( sizeof(struct file_des_str) );
        check_mem_exception ( file_des );

        setb ( 0, file_des, sizeof(struct file_des_str) );
        file_des->conn = temp_conn;

        if ( pathname != NULL ) then
            strcpy ( file_des->filename, pathname );
        else
            strcpy ( file_des->filename, "Unnamed file");

        send_node ( &open_file_list, file_des );

        /* Save the current file context, and then add a new file.          */

        save_context ( curr_file_ix );

        files_open    += 1;
        curr_file_ix  = files_open - 1;
        curr_file_des = file_des;

        if ( pathname != NULL ) then
            edit_file ( curr_file_ix );
        else
            edit_new_file ();

    end /* ve_exception == e_ok */

end /* add_file */

/* .page */

int file_menu_select ()

/****************************************************************************
 *                                                                          *
 *   Present a menu of the currently active files, and then read the user's *
 *   selection index of the file for editing.                               *
 *                                                                          *
 ****************************************************************************/

 begin

    int i, selection;

    /* Clear the screen and draw the menu. */

    if ( files_open == 1 ) then
        return ( 0 );

    move_cursor ( 0, 2 );
    writestr ( wclr_e );

    move_cursor ( 26, 3 );
    writestr ( "Files Currently Being Edited" );

    file_des = (struct file_des_str *) open_file_list.fwd_link;
    i = 0;

    while ( file_des != EOL ) begin

        move_cursor ( 5, 5 + i );

        sprintf ( temp.text, "%d   %s", i, &file_des->filename );
        writestr ( temp.text );

        file_des = (struct file_des_str *) file_des->fwd_link;
        i += 1;

    end /* while */


    /*  Now move the cursor to the bottom of the menu and read in the       */
    /*  response.  Note that this will never run off the page as the number */
    /*  of open files is limited to lines - 8                               */

    temp.length = prompt_read_str ( 5, 7+i, "Enter selection: ",
                                   -1, -1,  &temp.text );
    if ( temp.length == 0 ) then
        selection = ( curr_file_ix == -1 ? 0 : curr_file_ix );

    else begin

        sscanf ( temp.text, "%d", &selection );

        if ( selection < 0 || selection > i - 1 ) then
            selection = 0;
    end

    return ( selection );

end /* file_menu_select */

/* .page */

void edit_cmd ()

/****************************************************************************
 *                                                                          *
 *   Present the user with a menu of the available edit options, and then   *
 *   act on the selection.                                                  *
 *                                                                          *
 ****************************************************************************/

begin

    int new_ix;             /* Index of new file's descriptor               */
    int selection;          /* User's selection from file menu              */
    char key = ' ';         /* Input key                                    */
    int i;                  /* General purpose index                        */

    update_curr_line ();

    push_status_line ( "File  New  Menu  +" );

    while ( key != ESC ) begin

        /* Display the name of the file being edited on the status line.    */

        move_cursor ( 60, 0 );
        writestr ( subpath_of(curr_filename) );
        restore_cursor ();

        key = toupper (read_char());

        if ( key == ESC ) then
             break;

        if ( key == 'F' ) begin

            /*  Selection is File:  Add a new file to the current set, and  */
            /*  initiate editing of the new file.                           */

            if ( files_open >= lines - 6 ) begin

                write_status_line ( "Cannot open any more files" );
                pause ();

            end

            else begin

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

                if ( (new_ix = get_file (&temp.text)) != -1 ) then

                    /* If we already have the selected file, then get it    */
                    /* back as the current.                                 */

                    context_switch ( curr_file_ix, new_ix );

                else if ( temp.length != 0 ) then
                    add_file ( &temp.text );

                break;

            end

        end /* Key == F */



        else if ( key == 'N' ) begin

            /*  Selection is New:  Set up for the editing of a new, unnamed */
            /*  file, and add this to the list of active files.             */

            if ( files_open >= lines - 6 ) begin

                write_status_line ( "Cannot open any more files" );
                pause ();

            end

            else begin
                add_file ( NULL );
                break;
            end

        end /* Key == N */


        else if ( key == 'M' ) begin

            /* Selection is Menu:  Show the current menu, and then prompt   */
            /* for a selection from that menu.                              */

            if ( files_open == 0 ) begin

                write_status_line ( "No open files" );
                pause ();

            end

            else begin

                selection = file_menu_select ();
                context_switch ( curr_file_ix, selection );
                break;
            end

        end /* Key == M */


        else if ( key == '+' || key == '=' ) begin

            /*  Selection is + : Move to the next file, no matter what      */
            /*  file that is. (Allow = as well, as this is the lower case   */
            /*  of the + key.                                               */

            if ( files_open <= 1 ) begin

                write_status_line ( "Must have more than 1 file open" );
                pause ();

            end

            else begin

                new_ix = ( curr_file_ix + 1 ) % files_open;
                context_switch ( curr_file_ix, new_ix );

            end

        end /* Key == + */


        else begin

            /*  Invalid selection was chosen.                               */

            writechar ( BEL );

        end /* Invalid selection */


        /* Restore the edit command menu */

        write_status_line ( tos(&status_stack) );

    end /* while not ESC */


    pop_status_line ();    /* Restore master menu */

end /* edit_cmd */
line1 line1 line1
line2 line2 line2
line3 line3 line3
