/*++

Copyright (c) 1991  Microsoft Corporation

Module Name:

    regutil.c

Abstract:

    Utility routines for use by REGINI and REGDMP programs.
Author:

    Steve Wood (stevewo)  10-Mar-92

Revision History:

--*/

#include "regutil.h"

ULONG NumberOfLinesOutput;
BOOLEAN RegBackSwitches;
LPSTR SavedModuleName;
LPSTR SavedModuleUsage1;
LPSTR SavedModuleUsage2;
PHANDLER_ROUTINE SavedCtrlCHandler;

BOOL
CommonCtrlCHandler(
    IN ULONG CtrlType
    )
{
    if (CtrlType == CTRL_C_EVENT || CtrlType == CTRL_BREAK_EVENT) {
        if (SavedCtrlCHandler != NULL) {
            (*SavedCtrlCHandler)( CtrlType );
        }
        ExitProcess( 1 );
    }
    return FALSE;
}

void
InitCommonCode(
    PHANDLER_ROUTINE CtrlCHandler,
    LPSTR ModuleName,
    LPSTR ModuleUsage1,
    LPSTR ModuleUsage2
    )
{
    CONSOLE_SCREEN_BUFFER_INFO ConsoleScreenBufferInfo;

    if (!RTInitialize()) {
        FatalError( "Unable to initialize registry access functions (%u)",
                    GetLastError(), 0 );
        }

    if (_isatty( _fileno( stdout ) )) {
        if (GetConsoleScreenBufferInfo( GetStdHandle( STD_OUTPUT_HANDLE ),
                                        &ConsoleScreenBufferInfo
                                      )
           ) {
            OutputHeight = ConsoleScreenBufferInfo.srWindow.Bottom -
                           ConsoleScreenBufferInfo.srWindow.Top + 1;
            OutputWidth = ConsoleScreenBufferInfo.srWindow.Right -
                          ConsoleScreenBufferInfo.srWindow.Left + 1;
            }
        else {
            OutputHeight = 24;
            OutputWidth = 80;
            }
        }
    else {
        OutputHeight = 0;       // Dont pause on redirected output
        OutputWidth = 240;      // Larger width for redirected output
        }

    IndentMultiple = 4;
    SavedModuleName = ModuleName;
    if (!_stricmp( SavedModuleName, "REGBACK" )) {
        RegBackSwitches = TRUE;
        }
    else {
        RegBackSwitches = FALSE;
        }
    SavedModuleUsage1 = ModuleUsage1;
    SavedModuleUsage2 = ModuleUsage2;

    SetConsoleCtrlHandler( CtrlCHandler, TRUE );
    OldValueBufferSize = 30 * 4096;
    OldValueBuffer = VirtualAlloc( NULL, OldValueBufferSize, MEM_COMMIT, PAGE_READWRITE );
    if (OldValueBuffer == NULL) {
        FatalError( "Unable to allocate large value buffer (%u)",
                    GetLastError(), 0 );
        }

    return;
}

void
DisplayIndentedString(
    ULONG IndentAmount,
    PCHAR sBegin
    )
{
    PCHAR sEnd;

    while (sBegin != NULL) {
        sEnd = sBegin;
        while (*sEnd && *sEnd != '\n') {
            sEnd += 1;
            }

        MsgFprintf( stderr, "%.*s%.*s\n",
                 IndentAmount,
                 "                                                      ",
                 sEnd - sBegin, sBegin
               );

        NumberOfLinesOutput += 1;

        if (OutputHeight != 0 && NumberOfLinesOutput >= OutputHeight) {
            MsgFprintf( stderr, "more...press any key to continue" );
            TSGetch();
            MsgFprintf( stderr, "\r                                \r" );
            NumberOfLinesOutput = 2;
            }

        if (*sEnd == '\0') {
            break;
            }
        else {
            sBegin = ++sEnd;
            }
        }
    return;
}


void
Usage(
    LPSTR Message,
    ULONG_PTR MessageParameter
    )
{
    ULONG n;
    LPSTR sBegin, sEnd;

    NumberOfLinesOutput += 1;
    n = MsgFprintf( stderr, "usage: %s ", SavedModuleName );
    MsgFprintf( stderr, "[-m \\\\machinename" );
    if (!RegBackSwitches) {
        MsgFprintf( stderr, " | -h hivefile hiveroot | -w Win95 Directory" );
        }
    MsgFprintf( stderr, "]\n" );
    NumberOfLinesOutput += 1;
    if (!RegBackSwitches) {
        DisplayIndentedString( n, "[-i n] [-o outputWidth]" );
        }

    DisplayIndentedString( n, SavedModuleUsage1 );
    MsgFprintf( stderr, "\n" );
    NumberOfLinesOutput += 1;

    n = MsgFprintf( stderr, "where: " );
    MsgFprintf( stderr, "-m specifies a remote Windows NT machine whose registry is to be manipulated.\n" );
    NumberOfLinesOutput += 1;
    if (!RegBackSwitches) {
        DisplayIndentedString( n,
                               "-h specifies a specify local hive to manipulate.\n"
                               "-w specifies the paths to a Windows 95 system.dat and user.dat files\n"
                               "-i n specifies the display indentation multiple.  Default is 4\n"
                               "-o outputWidth specifies how wide the output is to be.  By default the\n"
                               "   outputWidth is set to the width of the console window if standard\n"
                               "   output has not been redirected to a file.  In the latter case, an\n"
                               "   outputWidth of 240 is used."
                             );
        MsgFprintf( stderr, "\n" );
        NumberOfLinesOutput += 1;
        }
    DisplayIndentedString( n, SavedModuleUsage2 );

    if (!RegBackSwitches) {
        DisplayIndentedString( n,
                               "Whenever specifying a registry path, either on the command line\n"
                               "or in an input file, the following prefix strings can be used:\n"
                               "\n"
                               "     HKEY_LOCAL_MACHINE\n"
                               "     HKEY_USERS\n"
                               "     HKEY_CURRENT_USER\n"
                               "     USER:\n"
                               "\n"
                               "   Each of these strings can stand alone as the key name or be followed\n"
                               "   a backslash and a subkey path."
                             );
        MsgFprintf( stderr, "\n" );
        NumberOfLinesOutput += 1;
        }

    //
    // No return from FatalError
    //

    if (Message != NULL) {
        MsgFprintf( stderr, "\n" );
        NumberOfLinesOutput += 1;
        }
    FatalError( Message, MessageParameter, 0 );
}

void
FatalError(
    LPSTR     Message,
    ULONG_PTR MessageParameter1,
    ULONG_PTR MessageParameter2
    )
{
    if (Message != NULL) {
        MsgFprintf( stderr, "%s: ", SavedModuleName );
        MsgFprintf( stderr, Message, MessageParameter1, MessageParameter2 );
        MsgFprintf( stderr, "\n" );
        }

    if (SavedCtrlCHandler != NULL) {
        (*SavedCtrlCHandler)( CTRL_BREAK_EVENT );
        }

    exit( 1 );
}

void
InputMessage(
    PWSTR FileName,
    ULONG LineNumber,
    BOOLEAN Error,
    LPSTR Message,
    ULONG_PTR MessageParameter1,
    ULONG_PTR MessageParameter2
    )
{
    MsgFprintf( stderr, "%ws(%u) : %s: ", FileName, LineNumber, Error ? "error" : "warning" );
    MsgFprintf( stderr, Message, MessageParameter1, MessageParameter2 );
    MsgFprintf( stderr, "\n" );
    return;
}

PWSTR
GetArgAsUnicode(
    LPSTR s
    )
{
    ULONG n;
    PWSTR ps;

    n = strlen( s );
    ps = HeapAlloc( GetProcessHeap(),
                    0,
                    (n + 1) * sizeof( WCHAR )
                  );
    if (ps == NULL) {
        FatalError( "Out of memory", 0, 0 );
        }

    if (MultiByteToWideChar( CP_ACP,
                             MB_PRECOMPOSED,
                             s,
                             n,
                             ps,
                             n
                           ) != (LONG)n
       ) {
        FatalError( "Unable to convert parameter '%s' to Unicode (%u)",
                    (ULONG_PTR)s, GetLastError() );
        }

    ps[ n ] = UNICODE_NULL;
    return ps;
}


void
CommonSwitchProcessing(
    PULONG argc,
    PCHAR **argv,
    CHAR c
    )
{
    c = (char)tolower( c );
    switch( c ) {
        case 'd':
            DebugOutput = TRUE;
            break;

        case 'o':
            if (--*argc) {
                OutputWidth = atoi( *++(*argv) );
                break;
                }
            else {
                Usage( "Missing argument to -o switch", 0 );
                }

        case 'i':
            if (--*argc) {
                IndentMultiple = atoi( *++(*argv) );
                break;
                }
            else {
                Usage( "Missing parameter for -%c switch", (ULONG)c );
                }

        case 'm':
            if (HiveFileName != NULL || HiveRootName != NULL ||
                Win95Path != NULL || Win95UserPath != NULL
               ) {
                Usage( "May only specify one of -h, -m or -w switches", 0 );
                }

            if (--*argc) {
                MachineName = GetArgAsUnicode( *++(*argv) );
                if (_wcsnicmp( MachineName, L"\\\\", 2 )) {
                    FatalError( "Invalid machine name - '%ws'",
                                (ULONG_PTR)MachineName, 0 );
                    }
                }
            else {
                Usage( "Missing parameter for -%c switch", (ULONG)c );
                }
            break;


        case 'w':
            if (MachineName != NULL || HiveFileName != NULL || HiveRootName != NULL) {
                Usage( "May only specify one of -h, -m or -w switches", 0 );
                }

            if (--*argc && --*argc) {
                Win95Path = GetArgAsUnicode( *++(*argv) );
                Win95UserPath = GetArgAsUnicode( *++(*argv) );
                }
            else {
                Usage( "Missing parameter(s) for -%c switch", (ULONG)c );
                }
            break;

        case 'h':
            if (MachineName != NULL || Win95Path != NULL || Win95UserPath != NULL) {
                Usage( "May only specify one of -h, -m or -w switches", 0 );
                }

            if (--*argc && --*argc) {
                HiveFileName = GetArgAsUnicode( *++(*argv) );
                HiveRootName = GetArgAsUnicode( *++(*argv) );
                }
            else {
                Usage( "Missing parameter(s) for -%c switch", (ULONG)c );
                }
            break;

        case '?':
            Usage( NULL, 0 );
            break;

        default:
            Usage( "Invalid switch (-%c)", (ULONG)c );
            break;
        }

    return;
}

/*
int MsgFprintf (
                 FILE *str,
                 LPSTR format,
                 ...
               )

if we ever need to display messages for english versions we can add the following portion of code.

[
#ifdef OUTPUT_ENGLISH_MSGS
   va_list va;
   if (PRIMARYLANGID(GetUserDefaultLangID()) == LANG_ENGLISH ) {
      va_start(va, format);
      i = vfprintf (str, format, va);
      va_end(va);
   }
#endif // OUTPUT_ENGLISH_MSGS
]
*/

int
__cdecl
MsgFprintf (
                 FILE *str,
                 LPSTR format,
                 ...
               )
{
   int i=0;
#ifndef SILENT_TS_TOOL
   va_list va;
   va_start(va, format);
   i = vfprintf (str, format, va);
   va_end(va);
#endif // SILENT_TS_TOOL
   return (i);
}
void TSGetch()
{
#ifndef SILENT_TS_TOOL
   _getch();
#endif // SILENT_TS_TOOL
}