
/******************************************************************************
**
**  This program is free software; you can redistribute it and/or
**  modify it, however, you cannot sell it.
**
**  This program is distributed in the hope that it will be useful,
**  but WITHOUT ANY WARRANTY; without even the implied warranty of
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
**
**  You should have received a copy of the license attached to the
**  use of this software.  If not, visit www.shmoo.com/osiris for
**  details.
**
******************************************************************************/

/*****************************************************************************
**
**  File:    get_user_input.c
**  Date:    April 2, 2004
**
**  Author:  Brian Wotring
**  Purpose: plain get user input and the fancy one.
**
******************************************************************************/


#include "libosiris.h"
#include "libosirism.h"
#include "libosirisdb.h"
#include "libosirisctl.h"
#include "libfileapi.h"
#include "version.h"
#include "osiris.h"

#ifdef WIN32
#include <conio.h>
#endif

#ifdef HAVE_TERMIOS_H
#include <termios.h>
#endif


#ifdef FANCY_CLI

#ifdef WIN32
#define KEY_CODE_ESC                224
#else
#define KEY_CODE_ESC                27
#endif /* WIN32 */
#define KEY_CODE_TAB                9
#ifdef WIN32
#define KEY_CODE_UP                 'H'
#define KEY_CODE_DOWN               'P'
#else
#define KEY_CODE_UP                 'A'
#define KEY_CODE_DOWN               'B'
#endif  /* WIN32 */
#define KEY_CODE_BACKSPACE          127
#define BACKSPACE_SEQUENCE          "\b \b"

#define CMD_PREVIOUS                1
#define CMD_NEXT                    2

extern CIRCLEQ_HEAD(cqhead, cmd) cq;
extern struct cmd *last_command;

int get_user_input( char *buffer, int buffer_size )
{
    int input;
    int output;

    size_t burst;

    char *end;
    char *p;
    unsigned char ch;

#ifdef HAVE_TERMIOS_H
    struct termios term;
    struct termios oterm;
#endif

    if( ( buffer == NULL ) || ( buffer_size <= 0 ) )
    {
        return 0;
    }

    fflush( stdout );
    memset( buffer, 0, buffer_size );

#ifdef HAVE_TERMIOS_H
    if( ( input = output = open( PATH_TTY, O_RDWR ) ) == -1 )
#endif
    {
        input = STDIN_FILENO;
        output = STDERR_FILENO;
    }

    /* turn off echo and intercept all keystrokes if possible.        */
    /* save current term settings so we can be nice and restore them. */

#ifdef HAVE_TERMIOS_H
    if( ( input != STDIN_FILENO ) && ( tcgetattr( input, &oterm ) == 0 ) )
    {
        memcpy( &term, &oterm, sizeof( term ) );
        term.c_cc[VMIN] = 1;
        term.c_cc[VTIME] = 0;
        term.c_lflag &= ~(ICANON|ISIG|ECHO);
        term.c_lflag &= (BRKINT);
        tcsetattr( input, TCSANOW, &term );
    }

    else
    {
        memset( &term, 0, sizeof( term ) );
        memset( &oterm, 0, sizeof( oterm ) );
    }
#endif

    /* set our boundries on the passed in buffer */

    p = buffer;
    end = ( buffer + buffer_size - 1 );

    /* start reading input, if the buffer already has input, back */
    /* up and print the buffer contents.                          */

start:

    /* backup our pointer, if we need to. */

    while( p > buffer )
    {
        fprintf( stdout, BACKSPACE_SEQUENCE );
        p--;
    }

    /* print new buffer. */

    fprintf( stdout, "%s",buffer );
    fflush( stdout );

    p += strlen( buffer );

#ifdef WIN32
    while( ( ch = getch() ) && ( ch != '\r' ) && ( ch != '\n' ) )
#else
    while( ( ( burst = read( input, &ch, 1 ) ) == 1 ) &&
             ( ch != '\n' ) && ( ch != '\r' ) )
#endif
    {
        /* first, we see if we have any special chars. */

        if( ch == KEY_CODE_ESC )
        {
            /* we have an arrow key, read the next two bytes.   */

#ifdef WIN32
            ch = getch();
#else
            read( input, &ch, 1 );
            read( input, &ch, 1 );
#endif

            /* up arrow. */

            if( ch == KEY_CODE_UP )
            {
                get_next_command( buffer, buffer_size, CMD_PREVIOUS );
                goto start;
            }

            if( ch == KEY_CODE_DOWN )
            {
                get_next_command( buffer, buffer_size, CMD_NEXT );
                goto start;
            }
        }

        else if( ch == KEY_CODE_TAB )
        {
            get_tab_completion( buffer, buffer_size );
            goto start;
        }

        else if(  ch == KEY_CODE_BACKSPACE )
        {
            if( p > buffer )
            {
                fprintf( stdout, BACKSPACE_SEQUENCE );
                fflush( stdout );
    
                p--;
                (*p) = '\0';
            }
        }

        /* nope, just a normal char. */

        else if( p < end )
        {
            fprintf( stdout, "%c", ch );
            fflush( stdout );
            (*p++) = ch;
        }
    }

    (*p) = '\0';

#ifdef HAVE_TERMIOS_H
    if( !( term.c_lflag & ECHO ) )
    {
        write( output, "\n", 1 );
    }

    /* restore old terminal settings if different. */

    if( memcmp( &term, &oterm, sizeof( term ) ) != 0 )
    {
        tcsetattr( input, TCSAFLUSH, &oterm );
    }
#else
    write( output, "\n", 1 );
#endif

    save_command( buffer );
    return strlen( buffer );
}

#else

/* non fancy get input routine. */

int get_user_input( char *buffer, int buffer_size )
{
    int length = 0;

    if( ( buffer != NULL ) && ( buffer_size > 0 ) )
    {
        if( fgets( buffer, ( buffer_size - 1 ), stdin ) != NULL )
        {
            int index;

            /* remove newline at end of input. */

            for( index = 0; (unsigned int)index < strlen( buffer ); index++ )
            {
                if( buffer[index] == '\n')
                {
                    buffer[index] = '\0';
                }
            }

            length = strlen( buffer );
        }
    }

    return length;
}
#endif

int get_password_input( char *buffer, int buffer_size, char *prompt )
{
#ifdef WIN32

    int ch;
    char *p;
    char *end;

    if( ( buffer == NULL ) || ( buffer_size <= 0 ) || ( prompt == NULL ) )
    {
        return 0;
    }

    end = ( buffer + buffer_size - 1 );
    p = buffer;

    memset( buffer, 0, buffer_size );

    fprintf( stdout, prompt );

    while( ( ch = getch() ) && ( ch != '\r' ) && ( ch != '\n' ) )
    {
        /* handle backspaces. */

        if( ( ch == '\b' ) && ( strlen( buffer ) > 0 ) )
        {
            fprintf( stdout, "\b \b" );
            buffer[strlen(buffer)-1] = '\0';
            p--;
        }

        if( ( p < end ) && ( ch != '\b' ) )
        {
            (*p++) = ch;
            putch( '*' );
        }
    }

    fprintf( stdout, "\n" );
    (*p) = '\0';

    return ( p - buffer );

#else

    int input;
    int output;

    size_t burst;

    char *end;
    char *p;
    char ch;

#ifdef HAVE_TERMIOS_H
    struct termios term;
    struct termios oterm;
#endif

    if( ( buffer == NULL ) || ( buffer_size <= 0 ) || ( prompt == NULL ) )
    {
        return 0;
    }

#ifdef HAVE_TERMIOS_H
    if( ( input = output = open( PATH_TTY, O_RDWR ) ) == -1 )
#endif
    {
        input = STDIN_FILENO;
        output = STDERR_FILENO;

        fprintf( stderr,
              "** unable to get tty! (using stdin), password will echo **\n" );
    }

    /* turn off echo if possible. */

#ifdef HAVE_TERMIOS_H
    if( ( input != STDIN_FILENO ) && ( tcgetattr( input, &oterm ) == 0 ) )
    {
        memcpy( &term, &oterm, sizeof( term ) );

        term.c_lflag &= ~( ECHO | ECHONL );
        tcsetattr( input, TCSAFLUSH, &term );
    }

    else
    {
        memset( &term, 0, sizeof( term ) );
        term.c_lflag |= ECHO;
        memset( &oterm, 0, sizeof( oterm ) );
        oterm.c_lflag |= ECHO;
    }
#endif

    write( output, prompt, strlen( prompt ) );

    /* now read the input. */

    end = ( buffer + buffer_size - 1 );

    for( p = buffer; ( ( burst = read( input, &ch, 1 ) ) == 1 ) &&
                       ( ch != '\n' ) && ( ch != '\r' ); )
    {
        if( p < end )
        {
            (*p++) = ch;
        }
    }

    (*p) = '\0';

#ifdef HAVE_TERMIOS_H
    if( !( term.c_lflag & ECHO ) )
    {
        write( output, "\n", 1 );
    }

    /* restore old terminal settings if different. */

    if( memcmp( &term, &oterm, sizeof( term ) ) != 0 )
    {
        tcsetattr( input, TCSAFLUSH, &oterm );
    }
#endif

    if( input != STDIN_FILENO )
    {
        close( input );
    }

    return ( p - buffer );

#endif /* WIN32 */
}


#ifdef FANCY_CLI

int save_command( const char *command )
{
    struct cmd *c;
    int length;

    if( command == NULL )
    {
        return -1;
    }

    if( strlen( command ) == 0 )
    {
        return 0;
    }

    c = (struct cmd *)malloc( sizeof( struct cmd ) );

    length = ( strlen( command ) + 1 );
    memset( c, 0, sizeof( struct cmd ) );
    c->command = (char *)malloc( length );
    osi_strlcpy( c->command, command, length );

    CIRCLEQ_INSERT_TAIL(&cq, c, next);
    last_command = NULL;

    return 0;
}

int get_next_command( char *buffer, int buffer_size, int direction )
{
    struct cmd *c = last_command;

    if( ( buffer == NULL ) || ( buffer_size <= 0 ) )
    {
        return -1;
    }

    if( direction == CMD_PREVIOUS )
    {
        if( last_command == NULL )
        {
            c = CIRCLEQ_LAST( &cq );
        }

        else if( last_command != CIRCLEQ_FIRST( &cq ) )
        {
            c = CIRCLEQ_PREV( last_command, next );
        }
    }

    else if( direction == CMD_NEXT )
    {
        if( last_command && last_command != CIRCLEQ_LAST( &cq ) )
        {
            c = CIRCLEQ_NEXT( last_command, next );
        }

        else
        {
            c = NULL;
        }
    }

    last_command = c;

    if( c == NULL )
    {
        buffer[0] = '\0';
        return -1;
    }

    osi_strlcpy( buffer, c->command, buffer_size );
    return 0;
}

int get_tab_completion( char *buffer, int buffer_size )
{
    int index;
    int found = 0;

    char *command_name = NULL;
    int buffer_len = strlen( buffer );

    if( ( buffer == 0 ) || ( strlen( buffer ) == 0 ) )
    {
        return -1;
    }

    for( index = 0; command_keywords[index].word != NULL; index++ )
    { 
        char *name = command_keywords[index].word;
    
        if( strncmp( buffer, name, buffer_len ) == 0 )
        {
            found++;
            command_name = name;
        }
    }

    /* single match */

    if( found == 1 )
    {
        osi_strlcpy( buffer, command_name, buffer_size );
        return 0;
    }

    /* more than one match, print the ones that do, leave buffer alone . */

    fprintf( stdout, "\n" );

    for( index = 0; command_keywords[index].word != NULL; index++ )
    { 
        char *name = command_keywords[index].word;
    
        if( strncmp( buffer, name, buffer_len ) == 0 )
        {
            fprintf( stdout, "  %s\n", name );
        }
    }

    fprintf( stdout, "\n" );
    print_prompt();
    fprintf( stdout, "%s", buffer );
    fflush( stdout );

    return 0;
}

#endif /* FANCY_CLI */
