#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  : main.c
 
                       VERSION : v3.5
 
                       REVISED : 31 Oct 2005
 
                       AUTHOR  : Michael A. Campbell
 
 
 
 
 
 


                          FUNCTIONAL DESCRIPTION
                          ----------------------
 
               This module contains the mainline of the VE editor.
 
               This mainline parses for command arguments, sets up
 
               the initial file (if any), initializes VE globals,
 
               and then enters a loop, always reading the keyboard.






*/

/* .page */

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

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

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

#define NON_NULL_CONN   1234

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

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

#include "../ext/modeproc.ext"
#include "../ext/editcmd.ext"
#include "../ext/optcmd.ext"
#include "../ext/termif.ext"
#include "../ext/lnklst.ext"
#include "../ext/veutils.ext"
#include "../ext/strprim.ext"
#include "../ext/stacks.ext"
#include "../ext/integrity.ext"
#include "../ext/columncmd.ext"

extern void exit ();

/* .page */

void edit_initial_files (argc, argv)

/***************************************************************************
 *                                                                         *
 *   If the user entered an argument, assume it is a file to be edited,    *
 *   and attempt to load it up.                                            *
 *                                                                         *
 ***************************************************************************/

int argc;         /* Number of command line arguments                      */
char *argv [];    /* Command strings from the command line                 */

begin
    register char dont_care;   /* Eat it and discard it                    */
    register int    i = 1;     /* argv[] index                             */
    char   notification[80];   /* Notification of open errors string       */
    char   display_fn[82];     /* Displayable version of filename          */

    /* Check for too many files being opened. If so, limit them and post   */
    /* an error message.                                                   */

    if ( argc > MAX_OPEN_FILES ) begin
       argc = MAX_OPEN_FILES + 1;
       write_status_line ("VE: Too many files - not all will be opened" );
       write_divider_msg ( "> Any key to continue", 0, 2 );
       dont_care = read_char ();
    end

    /* If any command line args were present, begin editing the indicated  */
    /* files. Else clear the all important control structures.             */

    while ( i < argc ) begin

        if ( !strcmp(argv[i], "-l") )
        begin
           /* A grotty little piece of code to detect someone who starts   */
           /* VE with the form "ve *.c -l line_num". This would end up     */
           /* adding "-l" and "file_num" as new files unless caught. To do */
           /* this right, startup argument processing needs to be done     */
           /* properly.                                                    */

           i = ( i + 2 >= argc ? i + 1 : i + 2 );
           break;
        end

        /* Catch the pathological case of a filename too large to fit into */
        /* curr_filename - likely someone trying to overflow the available */
        /* buffer.                                                         */

        if ( strlen(argv[i]) >= sizeof(curr_filename) )
           strcpy ( argv[i], "Filename-too-large.txt" );

        add_file (argv[i]);
        
        if (ve_exception != E_OK ) then
        begin
           /* Something went wrong with the file open. Figure out what,   */
           /* tell the user, and react correctly.                         */

            if ( ve_exception == E_OPEN_ERROR ) then
            begin
                /* The file does not exist. Assume the user wants to      */
                /* create a new file of this name, and proceed.           */

                add_file ( NULL );

                /* Build a message reflecting that is is a new file       */

                strcpy ( notification, "New File: " );
                truncate_str_left ( argv[1], display_fn, ERR_FIELD_WIDTH-12);
                strcat ( notification, display_fn );
                write_ve_msg ( notification );

                /* Load up the curr_filename var with the new file's name */
                /* and load up a dummy file connection, to fake out the   */
                /* quit command, and all of its subcommands.              */

                strcpy ( curr_filename, argv[i] );
                strcpy ( curr_file_des->filename, curr_filename );
                curr_conn = (FILE *) NON_NULL_CONN;
            end

            else
            begin
                /* All other possible errors, including that the file is   */
                /* too large for the available memory. Tell the user, and  */
                /* then skip the file.                                     */

                set_status_line ( TRUE, ve_exception, M_INSERT );
            end

        end /* something went wrong */

       i += 1;  /* advance to the next argument on the command line */

    end /* While i < argc */

    if ( argc == 1 || files_open == 0 ) begin

        /* No arguments entered, so simply open a null, unnamed file      */

        add_file ( NULL );
    end

    /* Print out initial file status message. If only one file opened  */
    /* display add_file's summary of the file size and text format. If */
    /* more than one, just say how many files were opened.             */

    if ( argc >= 3 ) begin
       sprintf ( ve_msg_buf, "%d %s", files_open, "files opened" );
       ve_msg = ve_msg_buf;
    end

end /* edit_initial_file */

/* .page */

void ve_init (argc, argv)

/***************************************************************************
 *                                                                         *
 *   Initialize Ve by setting up its data structures and optionally        *
 *   initializing the first file to be edited.                             *
 *                                                                         *
 ***************************************************************************/

int argc;         /* Number of command line arguments                      */
char *argv [];    /* Command strings from the command line                 */

begin
    int middle_width;         /* Width between fn and mode displays        */
    int remaining_cols;       /* Cols left after mode display, status line */
    int help_x;               /* X co-ord for help message                 */
    register int i;           /* Loop counter                              */
    int initial_line_num = 1; /* Line number in file to start at           */
    int successes;            /* scanf conversion successes                */
        
    ve_initializing = TRUE;  /* Some functions act differently during init */

    init_ve_globals ();      /* Set VE globals, load option file .ve.opt   */

    status = ti_set_char_mode ();  /* Place terminal into non echoing mode */

    /* Check display set status. If screen is not at least 20x80, display  */
    /* a message to that effect. If screen is wider than maximum supported */
    /* (defined in TERMIF.C), also display a message. In either case, wait */
    /* for the user to acknowledge it by entering any key and then exit.   */

    if ( status == 1 || status == 2 )
    begin
       if ( status == 1 )
          writestr ("VE requires at least 20 rows and 80 columns\n\n" );
       else
          writestr ("VE cannot support screen sizes this large\n\n"   );

       writestr ( "press any key to exit\n" );
       argc = read_char ();

       ti_reset_char_mode ();
       exit (0);
    end

    /* Compute the positioning of the VE Msg and Mode display fields based  */
    /* on display width variable "physical_cols", loaded by the             */
    /* ti_set_char_mode function. The mode display goes to the far right of */
    /* the available space, while the VE Msg display gets positioned in the */
    /* middle of the space between the filename display area and the mode   */
    /* display area. The "- 6 accounts for the options string, which goes   */
    /* just to the left of the mode area. For hysterical reasons, this      */
    /* string was never represented by a set of field descriptions.         */

    MODE_X = physical_cols - MODE_FIELD_WIDTH;
    MODE_Y = 0;

    remaining_cols = MODE_X - 6 - MSG_FIELD_WIDTH - MIN_FN_FIELD_WIDTH;

    MSG_X = ERR_X = MODE_X - MSG_FIELD_WIDTH - 6 - remaining_cols/2;
    MSG_Y = ERR_Y = 0;

    FN_FIELD_WIDTH = MSG_X - 2;
        
    alloc_reserve_memory ();
    init_undo_system ();
    init_stack (&status_stack, 10);
    init_column_block ();

    /* Put the cursor home and clear the screen, and set the colors to     */
    /* the default white text on a blue background                         */

    ti_clear_display ( 0 );
    ti_set_screen_colors ();

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

    ve_mode = last_ve_mode = M_INSERT;
    displayed_mode = M_NULL;

    /* Write out the divider line at second line (line 1) of the display */

    write_divider_line ( 1 );
    ti_move_cursor ( 0, 2 );

    /* Check for a "-h" or a "--help" initial argument. Put out the    */
    /* help panel if requested, and then clear the argument so that no */
    /* attempt is made to edit it as the initial file. Note that       */
    /* strcmp returns zero (FALSE) if the strings match, hence the "!" */

    if ( argc > 1 ) then
       if ( !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help") ) begin
           help_cmd ();

           mv_writestr (0, 0, FALSE, ve_masthead ); /* restore masthead */

           argc = 1;
       end

    /* Check for a "-b" option. If this is present, the user wants to start */
    /* VE in "browsing" mode - i.e. present a full screen list of the files */
    /* available to edit, so that they can select one. To achieve this,     */
    /* queue up an init macro of "ESC q r", which will cause VE to run the  */
    /* [Quit, Replace] command sequence as its initial command keys. This   */
    /* will discard the initial null file and present the Open File Dialog, */
    /* achieving the desired browsing result.                               */

    if ( argc > 1 )
       if ( !strcmp(argv[1], "-b") ) 
       begin
          init_macro[0]   = ESC; init_macro[1] = 'q'; init_macro[2] = 'r';
          init_macro_keys = 3;
          init_macro_ix   = 0;

          argc = 1;
       end

    if ( argc == 4 )
    begin
       /* Check for a command line of the type "ve fn -l number", which is  */
       /* a request to open the indicated file and move directly to the     */
       /* specified file number - VERY useful for moving directly to the    */
       /* line number provided by a compiler in an error message. 
       
       /* NOTE that that there is a grotty little bit of code in the        */
       /* edit_initial_files routine to capture and dispense with the "-l"  */
       /* argument if someone starts VE with "ve *.c -l line_num" - i.e.    */
       /* uses the "-l line_num" with more than one input filename.         */

       if ( !strcmp(argv[2], "-l") )
       begin
          /* Got a live one here! We did get a command line with an initial */
          /* line number. Convert the line number and then truncate the     */
          /* argument list so that the "-l number" isn't interpreted as     */
          /* file names to be opened.                                       */

          successes = sscanf ( argv[3], "%d", &initial_line_num );
          if ( ( initial_line_num <= 0 ) || ( successes != 1 ) )
             initial_line_num = 1;

          argc = 2;
       end
    end

    /* Open the initial files, if the user specified any on the command     */
    /* line; else, open a fresh unnamed file. Note that display of the      */
    /* opened files is suppressed during initialization to stop distracting */
    /* screen flashing. So when done, draw the current screen for the final */
    /* file opened.                                                         */

    edit_initial_files ( argc, argv );

    context_switch ( curr_file_ix, 0 );       /* Switch to the first file   */
    set_status_line ( TRUE, E_OK, M_INSERT ); /* and display its filename   */

    if ( initial_line_num == 1 )
       draw_curr_screen ();                   /* and display its contents   */

    else
    begin
       /* If we were asked to jump to an intial line number, do so,         */
       /* attempting to center the target line in the middle of the screen  */

       curr_line = goto_line_num ( initial_line_num - 1 );
       curr_screen.cy = update_curr_screen ( (lines-2) / 2 );
       draw_curr_screen ();
    end

    /* Put out masthead message. This is a one time event at initialization */
    /* and overwrites the file name display. It will be restored after the  */
    /* first key is pressed, by clear_ve_msg. The masthead message is NOT   */
    /* put out in the case of a multi file open, since it is important to   */
    /* see the file name display to know which of the multiple files that   */
    /* opened is being displayed.                                           */

    if ( argc < 3 ) begin
       for ( i = 0; i < FN_FIELD_WIDTH; i++ )
          temp.text[i] = ' ';
       temp.text[FN_FIELD_WIDTH] = '\0';

       mv_writestr ( 0, 0, FALSE, temp.text );        /* Clear field        */
       mv_writestr ( 0, 0, FALSE, ve_masthead );      /* Write out masthead */
    end

    /* In a similar vein to the masthead message, present a one time        */
    /* initial indication to the user that help is available via ESC-H.     */
    /* This overwrites normal status line information. That information     */
    /* will be restored after the first keystroke by clear_ve_msg. If no    */
    /* file has been opened, the help message is placed in the center of    */
    /* the screen, else it goes to the far right, so as to not overwrite    */
    /* the file statistics (chars, lines, format) that have been displayed. */
 
    init_help_shown = TRUE;
    help_x = (argc == 1) ? MSG_X : (MODE_X - 5);

    mv_writestr ( help_x, 0, FALSE, err_msg[E_OK] );

    /* Finally, signal the end of initialization, position the cursor in    */
    /* the text area and we are ready to rock n roll!                       */

    ve_initializing = FALSE;

    ti_move_cursor ( 0, curr_screen.cy );  /* Move to top of the text area  */

end /* ve_init */

/* .page */

int main (argc, argv)

/***************************************************************************
 *                                                                         *
 *   Ve mainline.  Forever read characters from the terminal and give them *
 *   to either the command processor or the visual processor.              *
 *                                                                         *
 ***************************************************************************/

int argc;        /* The number of command line args we are invoked with    */
char *argv[];    /* The arguments themselves                               */

begin
    register char key;

    ve_init (argc, argv);

    FOREVER begin

        /* Get a keystroke, clear any VE Messages generated by the last key */
        /* stroke, and then give the new key to either the cmd_processor or */
        /* the visual_processor, depending on mode.                         */

        key = read_char ();

        clear_ve_msg ();
        ve_exception = E_OK;

        if (ve_mode == M_COMMAND) then
            cmd_processor (key);

        else
            visual_processor (key);

        update_scroll_indicator ();

    end /* FOREVER */

end /* main */

