/*++

Copyright (c) 1991  Microsoft Corporation

Module Name:

    jzcommon.c

Abstract:

    This program contains the common routines for the Jazz setup program.

Author:

    David M. Robinson (davidro) 25-Oct-1991

Revision History:

--*/

#include "jzsetup.h"

//
// Static Data
//

CHAR VolatileEnvironment[LENGTH_OF_ENVIRONMENT];
BOOLEAN SetupIsRunning;
extern PCHAR Banner1, Banner2;

int
vsprintf (
    char *string,
    char *format,
    va_list arglist);

VOID
JzPrint (
    PCHAR Format,
    ...
    )

{

    va_list arglist;
    UCHAR Buffer[256];
    ULONG Count;
    ULONG Length;

    //
    // Format the output into a buffer and then print it.
    //

    va_start(arglist, Format);
    Length = vsprintf(Buffer, Format, arglist);

    ArcWrite( ARC_CONSOLE_OUTPUT, Buffer, Length, &Count);

    va_end(arglist);
    return 0;
}


ULONG
JxDisplayMenu (
    IN PCHAR Choices[],
    IN ULONG NumberOfChoices,
    IN LONG DefaultChoice,
    IN ULONG CurrentLine
    )

/*++

Routine Description:

Arguments:

Return Value:

    Returns -1 if the escape key is pressed, otherwise returns the menu item
    selected, where 0 is the first item.

--*/

{
    ULONG Index;
    UCHAR Character;
    ULONG Count;

    for (Index = 0; Index < NumberOfChoices ; Index++ ) {
        JzSetPosition( Index + CurrentLine, 10);
        if (Index == DefaultChoice) {
            JzSetScreenAttributes( TRUE, FALSE, TRUE);
            JzPrint(Choices[Index]);
            JzSetScreenAttributes( TRUE, FALSE, FALSE);
        } else {
            JzPrint(Choices[Index]);
        }
    }

    Character = 0;
    do {
        if (SetupIsRunning) {
            JzShowTime(FALSE);
        }
        if (ArcGetReadStatus(ARC_CONSOLE_INPUT) == ESUCCESS) {
            ArcRead(ARC_CONSOLE_INPUT, &Character, 1, &Count);
            switch (Character) {

            case ASCII_ESC:

                //
                // If there is another character available, look to see if
                // this a control sequence.  This is an attempt to make
                // Escape sequences from a terminal work.
                //

                JzStallExecution(10000);

                if (ArcGetReadStatus(ARC_CONSOLE_INPUT) == ESUCCESS) {
                    ArcRead(ARC_CONSOLE_INPUT, &Character, 1, &Count);
                    if (Character != '[') {
                        return(-1);
                    }
                } else {
                    return(-1);
                }

            case ASCII_CSI:
                ArcRead(ARC_CONSOLE_INPUT, &Character, 1, &Count);
                JzSetPosition( DefaultChoice + CurrentLine, 10);
                JzPrint(Choices[DefaultChoice]);
                switch (Character) {
                case 'A':
                case 'D':
                    DefaultChoice--;
                    if (DefaultChoice < 0) {
                        DefaultChoice = NumberOfChoices-1;
                    }
                    break;
                case 'B':
                case 'C':
                    DefaultChoice++;
                    if (DefaultChoice == NumberOfChoices) {
                        DefaultChoice = 0;
                    }
                    break;
                case 'H':
                    DefaultChoice = 0;
                    break;
                default:
                    break;
                }
                JzSetPosition( DefaultChoice + CurrentLine, 10);
                JzSetScreenAttributes( TRUE, FALSE, TRUE);
                JzPrint(Choices[DefaultChoice]);
                JzSetScreenAttributes( TRUE, FALSE, FALSE);
                continue;

            default:
                break;
            }
        }

    } while ((Character != '\n') && (Character != ASCII_CR));

    return DefaultChoice;
}


GETSTRING_ACTION
FwGetString(
    OUT PCHAR String,
    IN ULONG StringLength,
    IN PCHAR InitialString OPTIONAL,
    IN ULONG CurrentRow,
    IN ULONG CurrentColumn
    )

/*++

Routine Description:

    This routine reads a string from standardin until a carriage return is
    found, StringLength is reached, or ESC is pushed.

Arguments:

    String - Supplies a pointer to a location where the string is to be stored.

    StringLength - Supplies the Max Length to read.

    InitialString - Supplies an optional initial string.

    CurrentRow - Supplies the current screen row.

    CurrentColumn - Supplies the current screen column.

Return Value:

    If the string was successfully obtained GetStringSuccess is return,
    otherwise one of the following codes is returned.

    GetStringEscape - the escape key was pressed or StringLength was reached.
    GetStringUpArrow - the up arrow key was pressed.
    GetStringDownArrow - the down arrow key was pressed.

--*/

{
    ARC_STATUS Status;
    UCHAR c;
    ULONG Count;
    PCHAR Buffer;
    PCHAR Cursor;
    PCHAR CopyPointer;
    GETSTRING_ACTION Action;
    BOOLEAN CarriageReturn;

    //
    // If an initial string was supplied, update the output string.
    //

    if (ARGUMENT_PRESENT(InitialString)) {
        strcpy(String, InitialString);
        Buffer = strchr(String, 0);
    } else {
        *String = 0;
        Buffer = String;
    }

    Cursor = Buffer;

    while (TRUE) {

        //
        // Flag to print a carriage return/line feed when all is done.
        //

        CarriageReturn = FALSE;

        //
        // Print the string.
        //

        JzSetPosition(CurrentRow, CurrentColumn);
        JzPrint(String);
        JzPrint("\x9bK");

        //
        // Print the cursor.
        //

        JzSetScreenAttributes(TRUE,FALSE,TRUE);
        JzSetPosition(CurrentRow, (Cursor - String) + CurrentColumn);
        if (Cursor >= Buffer) {
            JzPrint(" ");
        } else {
            ArcWrite(ARC_CONSOLE_OUTPUT,Cursor,1,&Count);
        }
        JzSetScreenAttributes(TRUE,FALSE,FALSE);

        while (ArcGetReadStatus(ARC_CONSOLE_INPUT) != ESUCCESS) {
            if (SetupIsRunning) {
                JzShowTime(FALSE);
            }
        }
        Status = ArcRead(ARC_CONSOLE_INPUT,&c,1,&Count);

        if (Status != ESUCCESS) {
            Action = GetStringEscape;
            goto EndGetString;
        }

        if (Buffer-String == StringLength) {
            Action = GetStringEscape;
            goto EndGetString;
        }

        switch (c) {
        case ASCII_ESC:

            //
            // If there is another character available, look to see if
            // this a control sequence, and fall through to ASCII_CSI.
            // This is an attempt to make escape sequences from a terminal work.
            //

            JzStallExecution(10000);

            if (ArcGetReadStatus(ARC_CONSOLE_INPUT) == ESUCCESS) {
                ArcRead(ARC_CONSOLE_INPUT, &c, 1, &Count);
                if (c != '[') {
                    Action = GetStringEscape;
                    goto EndGetString;
                }
            } else {
                Action = GetStringEscape;
                goto EndGetString;
            }

        case ASCII_CSI:

            ArcRead(ARC_CONSOLE_INPUT, &c, 1, &Count);
            switch (c) {

            case 'A':
                Action = GetStringUpArrow;
                goto EndGetString;

            case 'D':
                if (Cursor != String) {
                    Cursor--;
                }
                continue;

            case 'B':
                Action = GetStringDownArrow;
                goto EndGetString;

            case 'C':
                if (Cursor != Buffer) {
                    Cursor++;
                }
                continue;

            case 'H':
                Cursor = String;
                continue;

            case 'K':
                Cursor = Buffer;
                continue;

            case 'P':
                CopyPointer = Cursor;
                while (*CopyPointer) {
                    *CopyPointer = *(CopyPointer + 1);
                    CopyPointer++;
                }
                if (Buffer != String) {
                    Buffer--;
                }
                continue;

            default:
                break;
            }
            break;

        case '\r':
        case '\n':

            CarriageReturn = TRUE;
            Action = GetStringSuccess;
            goto EndGetString;

        case '\b':

            if (Cursor != String) {
                Cursor--;
            }
            CopyPointer = Cursor;
            while (*CopyPointer) {
                *CopyPointer = *(CopyPointer + 1);
                CopyPointer++;
            }
            if (Buffer != String) {
                Buffer--;
            }
            break;

        default:

            //
            // Store the character.
            //

            CopyPointer = ++Buffer;
            if (CopyPointer > Cursor) {
                while (CopyPointer != Cursor) {
                    *CopyPointer = *(CopyPointer - 1);
                    CopyPointer--;
                }
            }
            *Cursor++ = c;
            break;
        }
    }
    Action = GetStringEscape;

EndGetString:

    //
    // Clear the cursor.
    //

    JzSetPosition(CurrentRow, (Cursor - String) + CurrentColumn);
    if (Cursor >= Buffer) {
        JzPrint(" ");
    } else {
        ArcWrite(ARC_CONSOLE_OUTPUT,Cursor,1,&Count);
    }

    if (CarriageReturn) {
        ArcWrite(ARC_CONSOLE_OUTPUT,JZ_CRLF_MSG,2,&Count);
    }

    //
    // Make sure we return a null string if not successful.
    //

    if (Action != GetStringSuccess) {
        *String = 0;
    }

    return(Action);
}


ARC_STATUS
FwEnvironmentCheckChecksum (
    VOID
    )

/*++

Routine Description:

    This routine checks the environment area checksum.


Arguments:

    None.

Return Value:

    If the checksum is good, ESUCCESS is returned, otherwise EIO is returned.

--*/

{
    PUCHAR NvChars;
    PNV_CONFIGURATION NvConfiguration;
    ULONG Index;
    ULONG Checksum1, Checksum2;

    NvConfiguration = (PNV_CONFIGURATION)NVRAM_CONFIGURATION;

    //
    // Form checksum from NVRAM data.
    //

    Checksum1 = 0;
    NvChars = (PUCHAR)&NvConfiguration->Environment[0];

    for ( Index = 0 ;
    Index < LENGTH_OF_ENVIRONMENT;
    Index++ ) {
        Checksum1 += READ_REGISTER_UCHAR( NvChars++ );
    }

    //
    // Reconstitute checksum and return error if no compare.
    //

    Checksum2 = (ULONG)READ_REGISTER_UCHAR( &NvConfiguration->Checksum2[0] ) |
                (ULONG)READ_REGISTER_UCHAR( &NvConfiguration->Checksum2[1] ) << 8 |
                (ULONG)READ_REGISTER_UCHAR( &NvConfiguration->Checksum2[2] ) << 16 |
                (ULONG)READ_REGISTER_UCHAR( &NvConfiguration->Checksum2[3] ) << 24 ;

    if (Checksum1 != Checksum2) {
        return EIO;
    } else {
        return ESUCCESS;
    }
}

PCHAR
FwEnvironmentLoad (
    VOID
    )

/*++

Routine Description:

    This routine loads the entire environment into a volatile environment
    area.


Arguments:

    None.

Return Value:

    If the checksum is good, a pointer to the environment in returned,
    otherwise NULL is returned.

--*/

{
    ULONG Index;
    PUCHAR NvChars;
    PUCHAR VChars;
    PNV_CONFIGURATION NvConfiguration;

    if (FwEnvironmentCheckChecksum() == ESUCCESS) {

        NvConfiguration = (PNV_CONFIGURATION)NVRAM_CONFIGURATION;

        //
        // Copy the data into the volatile area.
        //

        NvChars = (PUCHAR)&NvConfiguration->Environment[0];
        VChars = (PUCHAR)&VolatileEnvironment;

        for ( Index = 0 ;
        Index < LENGTH_OF_ENVIRONMENT;
        Index++ ) {
            *VChars++ = READ_REGISTER_UCHAR( NvChars++ );
        }

        return (PCHAR)&VolatileEnvironment;
    } else {
        return NULL;
    }
}


BOOLEAN
FwGetPathMnemonicKey(
    IN PCHAR OpenPath,
    IN PCHAR Mnemonic,
    IN PULONG Key
    )

/*++

Routine Description:

    This routine looks for the given Mnemonic in OpenPath.
    If Mnemonic is a component of the path, then it converts the key
    value to an integer wich is returned in Key.

Arguments:

    OpenPath - Pointer to a string that contains an ARC pathname.

    Mnemonic - Pointer to a string that contains a ARC Mnemonic

    Key      - Pointer to a ULONG where the Key value is stored.


Return Value:

    FALSE  if mnemonic is found in path and a valid key is converted.
    TRUE   otherwise.

--*/

{

    PCHAR Tmp;
    CHAR  Digits[4];
    ULONG i;
    CHAR  String[16];

    //
    // Construct a string of the form ")mnemonic("
    //
    String[0]=')';
    for(i=1;*Mnemonic;i++) {
        String[i] = * Mnemonic++;
    }
    String[i++]='(';
    String[i]='\0';

    if ((Tmp=strstr(OpenPath,&String[1])) == NULL) {
        return TRUE;
    }

    if (Tmp != OpenPath) {
        if ((Tmp=strstr(OpenPath,String)) == NULL) {
            return TRUE;
        }
    } else {
        i--;
    }
    //
    // skip the mnemonic and convert the value in between parentesis to integer
    //
    Tmp+=i;
    for (i=0;i<3;i++) {
        if (*Tmp == ')') {
            Digits[i] = '\0';
            break;
        }
        Digits[i] = *Tmp++;
    }
    Digits[i]='\0';
    *Key = atoi(Digits);
    return FALSE;
}


VOID
FwWaitForKeypress(
    VOID
    )

/*++

Routine Description:

    This routine waits for a keypress, then returns.

Arguments:

    None.

Return Value:

    None.

--*/

{
    UCHAR Character;
    ULONG Count;

    JzPrint(JZ_PRESS_KEY_MSG);
    while (ArcGetReadStatus(ARC_CONSOLE_INPUT) != ESUCCESS) {
        if (SetupIsRunning) {
            JzShowTime(FALSE);
        }
    }
    ArcRead(ARC_CONSOLE_INPUT, &Character, 1, &Count);
    JzPrint("%c2J", ASCII_CSI);
}

BOOLEAN
FwGetVariableSegment (
    IN ULONG SegmentNumber,
    IN OUT PCHAR Segment
    )

/*++

Routine Description:

     This routine returns the specified segment of an environment variable.
     Segments are separated by semicolons.

Arguments:

     SegmentNumber - Supplies the number of the segment to return.

     Segment - Supplies a pointer to the name of the environment variable.
               The variable may be followed by an equal sign.
               An '=' and the segment value are appended to this name and
               returned.


Return Value:

     If one or more segments exist after the specified segment, TRUE is returned,
     otherwise FALSE is returned.

--*/

{
    ULONG Index;
    ULONG Count;
    PCHAR VariableValue;
    PCHAR TempPointer;

    //
    // Remove an equal sign if present.
    //

    if ((TempPointer = strchr(Segment, '=')) != NULL) {
        *TempPointer = 0;
    }

    //
    // Get variable, add equal sign, and advance Segment to where the value
    // is to be added.
    //

    VariableValue = ArcGetEnvironmentVariable(Segment);
    strcat(Segment, "=");
    Segment = strchr(Segment, '=') + 1;

    //
    // If there was no variable, return.
    //

    if (VariableValue == NULL) {
        return(FALSE);
    }

    Index = 0;
    Count = 0;

    //
    // Search for the requested segment and copy it to the return value.
    //

    while ((TempPointer = strchr(VariableValue,';')) != NULL) {
        Count = TempPointer - VariableValue;

        if (Index == SegmentNumber) {
            strncpy(Segment, VariableValue, Count);
            Segment[Count] = 0;
            return TRUE;
        }

        VariableValue += Count + 1;
        Index++;
    }

    //
    // If there is data left, copy it to the return value.
    //

    strcpy(Segment,VariableValue);

    return(FALSE);
}

ARC_STATUS
FwSetVariableSegment (
    IN ULONG SegmentNumber,
    IN PCHAR VariableName,
    IN PCHAR NewSegment
    )

/*++

Routine Description:

     This routine sets the specified segment of an environment variable.
     Segments are separated by semicolons.

Arguments:

     SegmentNumber - Supplies the number of the segment to add.

     VariableName - Supplies a pointer to the name of the environment variable.
                    The variable may be followed by an equal sign.

     NewSegment - Supplies a pointer to the new segment.

Return Value:

     Returns ESUCCESS is the segment is set, otherwise an error code is
     returned.

--*/

{
    ARC_STATUS Status;
    PCHAR Variable;
    PCHAR NextVariable;
    PCHAR EndOfVariable;
    CHAR VariableValue[256];
    ULONG Index;
    ULONG Count;

    Variable = ArcGetEnvironmentVariable(VariableName);
    VariableValue[0] = 0;

    if (Variable != NULL) {
        EndOfVariable = strchr(Variable, 0);
        for (Index = 0; Index < SegmentNumber ; Index++ ) {
            NextVariable = strchr(Variable, ';');
            if (NextVariable != NULL) {
                Count = NextVariable - Variable + 1;
                strncat(VariableValue, Variable, Count);
                Variable = NextVariable + 1;
            } else {
                strcat(VariableValue, Variable);
                Variable = EndOfVariable;
                strcat(VariableValue, ";");
            }
        }
    } else {
        for (Index = 0; Index < SegmentNumber ; Index++ ) {
            strcat(VariableValue, ";");
        }
    }

    strcat(VariableValue, NewSegment);

    if ((Variable != NULL) && (Variable != EndOfVariable)) {
        strcat(VariableValue, ";");
        strcat(VariableValue, Variable);
    }

    Status = ArcSetEnvironmentVariable(VariableName, VariableValue);
    return(Status);
}


ULONG
JzGetSelection (
    IN PCHAR Menu[],
    IN ULONG NumberOfChoices,
    IN ULONG DefaultChoice
    )

/*++

Routine Description:

    This routine gets a menu selection from the user.

Arguments:

    Menu - Supplies an array of pointers to menu character strings.

    Selections - Supplies the number of menu selections.

    DefaultChoice - Supplies the current default choice.

Return Value:

    Returns the value selected, -1 if the escape key was pressed.

--*/
{
    ULONG CurrentLine;
    ULONG Index;

    //
    // Clear screen and print banner.
    //

    JzSetScreenAttributes( TRUE, FALSE, FALSE);
    JzPrint("%c2J", ASCII_CSI);

    CurrentLine = 0;
    JzSetPosition( CurrentLine++, 0);

    JzPrint(Banner1);
    JzSetPosition( CurrentLine++, 0);
    JzPrint(Banner2);
    JzShowTime(TRUE);

    CurrentLine += NumberOfChoices + 3;
    JzSetPosition(CurrentLine, 0);

    //
    // Display the menu and the wait for an action to be selected.
    //

    DefaultChoice = JxDisplayMenu(Menu,
                                  NumberOfChoices,
                                  DefaultChoice,
                                  3);

    //
    // Clear the choices.
    //

    for (Index = 0; Index < NumberOfChoices ; Index++ ) {
        JzSetPosition( Index + 3, 5);
        JzPrint("%cK", ASCII_CSI);
    }

    return(DefaultChoice);
}
