//================================================================================
// Copyright (C) 1997 Microsoft Corporation
// Author: RameshV
// Description: generic testing engine
//================================================================================

//===============================
//  headers
//===============================
#include    <hdrmacro.h>
#include    <store.h>
#include    <dhcpmsg.h>
#include    <wchar.h>
#include    <dhcpbas.h>
#include    <mm\opt.h>
#include    <mm\optl.h>
#include    <mm\optdefl.h>
#include    <mm\optclass.h>
#include    <mm\classdefl.h>
#include    <mm\bitmask.h>
#include    <mm\reserve.h>
#include    <mm\range.h>
#include    <mm\subnet.h>
#include    <mm\sscope.h>
#include    <mm\oclassdl.h>
#include    <mm\server.h>
#include    <mm\address.h>
#include    <mm\server2.h>
#include    <mm\memfree.h>
#include    <mmreg\regutil.h>
#include    <mmreg\regread.h>
#include    <mmreg\regsave.h>
#include    <dhcpread.h>
#include    <dhcpapi.h>
#include    <rpcapi1.h>
#include    <rpcapi2.h>

//BeginExport(typedefs)
// supported data types are listed. any new datatypes should be added here.
typedef     enum /* anonymous */ {
    Char,
    wChar,
    Word,
    DWord,
    String,
    wString,
    IpAddress,
    xBytes,
    Invalid
}   NJIN_TYPES, *LPNJIN_TYPES;

typedef    struct /* anonymous */ {
    union         /* anonymous */ {
        CHAR                      *Char;
        WCHAR                     *wChar;
        WORD                      *Word;
        DWORD                     *DWord;
        LPSTR                      String;
        LPWSTR                     wString;
        DWORD                     *IpAddress;
        struct    /* anonymous */ {
            DWORD                 *xBytesLen;
            LPBYTE                 xBytesBuf;
        };
    };
}   NJIN_DATA, *LPNJIN_DATA;

typedef     struct /* anonymous */ {
    LPWSTR                         VarName;
    NJIN_TYPES                     VarType;
    NJIN_DATA                      VarData;
    VOID                           (*VarFunc)(LPWSTR VarName);
    BOOL                           ReadOnly;
}   NJIN_VAR, *LPNJIN_VAR;

typedef     struct /* anonymous */ {
    WCHAR                          cComment;
    WCHAR                          cEquals;
    WCHAR                          cReturn;
    WCHAR                          cEndOfLine;
    WCHAR                          cCharFmtActual;
    WCHAR                          cCharFmtNum;
    BOOL                           fEcho;
    BOOL                           fVerbose;
}   NJIN_ENV, *LPNJIN_ENV;
//EndExport(typedefs)

#define     verbose                if( Env->fVerbose) printf
#define     echo                   if( Env->fEcho) printf

DWORD
ConvertToIntegral(
    IN      LPWSTR                 Str
)
{
    return _wtol(Str);
}

DWORD
ConvertToAddress(
    IN      LPWSTR                 Str
)
{
    CHAR                           Buf[1000];
    LPSTR                          s;

    s = Buf;
    while(*s ++ = (CHAR) *Str++ )
        /* skip */;

    return inet_addr(Buf);
}

DWORD
CloneWString(
    IN      LPWSTR                 Buf,
    IN      LPWSTR                 Dest
)
{
    wcscpy(Dest, Buf);
    return ERROR_SUCCESS;
}

DWORD
CloneString(
    IN      LPWSTR                 Str,
    IN      LPSTR                  s
)
{
    while(*s ++ = (CHAR) *Str ++ )
        /* skip */;

    return ERROR_SUCCESS;
}

BYTE
Hex(
    IN      WCHAR                  C
)
{
    if( C >= L'0' && C <= L'9' ) return C - L'0';
    if( C >= L'A' && C <= L'F' ) return C - L'A' + 10;
    if( C >= L'a' && C <= L'f' ) return C - L'a' + 10;
    return 0x55;
}

DWORD
ConvertToHexBytes(
    IN      LPWSTR                 Str,
    IN OUT  LPBYTE                 Buf,
    IN OUT  LPDWORD                BufLen
)
{
    BYTE                           ch, ch2;

    *BufLen = 0;
    while( (ch = Hex(*Str++)) <= 0xF) {
        ch2 = Hex(*Str++);
        if( ch2 > 0xF ) {
            printf("illegal character found, ignored\n");
            ch2 = 0;
        }
        Buf[(*BufLen)++] = (ch <<4) | ch2;
    }

    return ERROR_SUCCESS;
}

DWORD
ConvertDataType(
    IN      LPNJIN_ENV             Env,
    IN      LPWSTR                 Buf,
    IN      DWORD                  VarType,
    IN      NJIN_DATA             *VarData
)
{
    switch(VarType) {
    case Char:
        if( Env->cCharFmtActual == Buf[0] ) {
            *VarData->Char = (CHAR) Buf[1];
        } else if( Env->cCharFmtNum == Buf[0] ) {
            *VarData->Char = (CHAR) ConvertToIntegral(&Buf[1]);
        } else return ERROR_INVALID_DATA;
        return ERROR_SUCCESS;
    case wChar:
        if( Env->cCharFmtActual == Buf[0] ) {
            *VarData->wChar = Buf[1];
        } else if( Env->cCharFmtNum == Buf[0] ) {
            *VarData->wChar = (WCHAR) ConvertToIntegral(&Buf[1]);
        } else return ERROR_INVALID_DATA;
        return ERROR_SUCCESS;
    case Word:
        *VarData->Word = (WORD) ConvertToIntegral(Buf);
        return ERROR_SUCCESS;
    case DWord:
        *VarData->DWord = ConvertToIntegral(Buf);
        return ERROR_SUCCESS;
    case wString:
        return CloneWString(Buf, VarData->wString);
    case String:
        return CloneString(Buf, VarData->String);
    case IpAddress:
        *VarData->IpAddress = ConvertToAddress(Buf);
        return ERROR_SUCCESS;
    case xBytes:
        return ConvertToHexBytes(Buf, VarData->xBytesBuf, VarData->xBytesLen);
    default:
        verbose("unknown variable type\n");
        return ERROR_INVALID_DATA;
    }
}

//BeginExport(function)
DWORD
NjinExecuteLineA(
    IN      LPNJIN_ENV             Env,
    IN OUT  LPNJIN_VAR             Vars,
    IN      DWORD                  nVars,
    IN      LPWSTR                 Cmd
) //EndExport(function)
{
    CHAR                           Ch, LastCh;
    DWORD                          i, Result;
    LPWSTR                         Var;
    LPWSTR                         Value;

    Var = NULL; Value = NULL;
    for( i = 0; Cmd[i] != L'\0' ; i ++ ) {
        if( Env->cEquals == Cmd[i] ) break;
    }

    if( Cmd[i] ) {
        Cmd[i] = L'\0';
        Var = Cmd;
        Value = &Cmd[i+1];
    } else {
        verbose("No parameter passed\n");
        return ERROR_INVALID_DATA;
    }

    for( i = 0; i < nVars; i ++ ) {
        if( !Vars[i].ReadOnly && 0 == wcscmp(Vars[i].VarName, Var) )
            break;
    }

    if( i >= nVars ) {
        verbose("No such variable or operation\n");
        return ERROR_FILE_NOT_FOUND;
    }

    Result = ConvertDataType(Env, Value, Vars[i].VarType, &Vars[i].VarData);
    if( ERROR_SUCCESS != Result ) {
        verbose("ConvertDataType: 0x%lx (%ld)\n", Result, Result );
        return Result;
    }

    if( Vars[i].VarFunc ) Vars[i].VarFunc(Vars[i].VarName );
    return ERROR_SUCCESS;
}

//BeginExport(function)
DWORD
NjinExecute(
    IN      LPNJIN_ENV             Env,
    IN OUT  LPNJIN_VAR             Vars,
    IN      DWORD                  nVars
) //EndExport(function)
{
    char                           ch, lastch;
    WCHAR                          buffer[1000];
    unsigned int                   i;
    DWORD                          Result;

    verbose("NjinExecute called\n");

    i = 0; lastch = (CHAR)Env->cEndOfLine;
    while ((ch = getchar()) != EOF ) {
        if( Env->cReturn == ch ) ch = (CHAR)Env->cEndOfLine;
        if( Env->cEndOfLine == lastch && Env->cComment == ch ) {
            while((ch = getchar()) != EOF && Env->cEndOfLine != ch )
                ;
            lastch = ch;
            continue;
        }

        if( Env->cEndOfLine == ch ) {
            if( i ) {
                buffer[i] = L'\0';
                Result = NjinExecuteLineA(Env,Vars,nVars, buffer);
                if( ERROR_SUCCESS != Result ) {
                    verbose("Error: %ld (0x%lx)\n", Result, Result);
                return Result;
                }
            }
            i = 0;
        } else {
            buffer[i++] = (WCHAR)ch;
        }
        lastch = ch;
    }

    return ERROR_SUCCESS;
}

//================================================================================
//  DHCP SNAPIN DS WORK -- helper routines
//================================================================================

STORE_HANDLE                       hStore1, hStore2, hStore3, hStore4;
LPSTORE_HANDLE                     hRoot = NULL, hDhcpC = NULL, hObject = NULL, hDhcpRoot = NULL;
WCHAR                              gDsServer[100], gObject[100], gName[100], gComment[100];
WCHAR                              gClassName[100], gClassComment[100];
WCHAR                              Dummy[100];
WCHAR                              gOptName[100], gOptComment[100];
WCHAR                              gServerName[256];

DWORD                              gServerAddress, gServerState;
DWORD                              gOptId, gOptType, gOptLen, gIsVendor;
DWORD                              gDwordOptData;
BYTE                               gOptVal[255], gClassData[255];
DWORD                              gClassDataLen;

VOID
print_DHCP_OPTION_DATA(
    IN      DWORD                  nTabs,
    IN      LPDHCP_OPTION_DATA     OptData
)
{
    DWORD                          i;

    i = nTabs;
    while(i--) putchar('\t');

    printf("NumElements=%ld, Type=%ld [0x%lx]\n",
           OptData->NumElements, OptData->Elements[0].OptionType, OptData->Elements[0].Element.DWordOption
    );
}

VOID
print_DHCP_OPTION(
    IN      DWORD                  nTabs,
    IN      LPDHCP_OPTION          Option
)
{
    DWORD                          i;

    i = nTabs;
    while(i--) putchar('\t');

    printf("Option [x%03x] %ws %ws Type= %ld\n",
           Option->OptionID, Option->OptionName, Option->OptionComment?Option->OptionComment:L"", Option->OptionType
    );

    print_DHCP_OPTION_DATA(nTabs+1, &Option->DefaultValue);
}

VOID
print_DHCP_OPTION_ARRAY(
    IN      DWORD                  nTabs,
    IN      LPDHCP_OPTION_ARRAY    OptArray
)
{
    DWORD                          i;

    i = nTabs;
    while(i--) putchar('\t');
    printf("NumElements= %ld\n", OptArray->NumElements);

    for( i = 0; i < OptArray->NumElements; i++ )
        print_DHCP_OPTION(nTabs+1, &OptArray->Options[i]);
}

VOID
print_DHCP_OPTION_VALUE(
    IN      DWORD                  nTabs,
    IN      LPDHCP_OPTION_VALUE    OptVal
)
{
    DWORD                          i;

    i = nTabs;
    while(i--) putchar('\t');

    printf("OptionId=%ld\n", OptVal->OptionID);
    print_DHCP_OPTION_DATA(nTabs+1, &OptVal->Value);
}

VOID
print_DHCP_OPTION_VALUE_ARRAY(
    IN      DWORD                  nTabs,
    IN      LPDHCP_OPTION_VALUE_ARRAY  OptValues
)
{
    DWORD                          i;

    i = nTabs;
    while(i--) putchar('\t');

    printf("NumElements=%ld\n", OptValues->NumElements);
    for( i = 0; i < OptValues->NumElements ; i ++ )
        print_DHCP_OPTION_VALUE(nTabs+1, &OptValues->Values[i]);
}

VOID
print_DHCP_CLASS_INFO(
    IN      DWORD                  nTabs,
    IN      LPDHCP_CLASS_INFO      ClassInfo
)
{
    DWORD                          i;

    i = nTabs;
    while(i--) putchar('\t');

    printf("ClassName= %ws ClassComment= %ws DataLength = %ld\n",
           ClassInfo->ClassName, ClassInfo->ClassComment, ClassInfo->ClassDataLength
    );
}

VOID
print_DHCP_CLASS_INFO_ARRAY(
    IN      DWORD                  nTabs,
    IN      LPDHCP_CLASS_INFO_ARRAY ClassInfoArray
)
{
    DWORD                          i;

    i = nTabs;
    while(i--) putchar('\t');

    printf("NumElements=%ld\n", ClassInfoArray->NumElements);
    for( i = 0; i < ClassInfoArray->NumElements ; i ++ )
        print_DHCP_CLASS_INFO(nTabs+1, &ClassInfoArray->Classes[i]);
}

VOID
print_DHCPDS_SERVER(
    IN      DWORD                  nTabs,
    IN      LPDHCPDS_SERVER        Server
)
{
    DWORD                          i;

    i = nTabs;
    while(i--) putchar('\t');

    printf("Name=[%ws] State=%ld Address=%s Loc=[%ws] Type=%ld\n",
           Server->ServerName, Server->Flags, inet_ntoa(*(struct in_addr *)&Server->IpAddress),
           Server->DsLocation? Server->DsLocation: L"(NULL)",
           Server->DsLocType
    );
}

VOID
print_DHCPDS_SERVERS(
    IN      DWORD                  nTabs,
    IN      LPDHCPDS_SERVERS       Servers
)
{
    DWORD                          i;

    i = nTabs;
    while(i--) putchar('\t');

    printf("NumElements=%ld\n", Servers->NumElements);
    for( i = 0; i < Servers->NumElements ; i ++ )
        print_DHCPDS_SERVER(nTabs+1, &Servers->Servers[i]);
}

VOID
SetDsServer(
    IN      LPWSTR                 VarNameUnused
)
{
    DWORD                          Err;

    if( NULL != hRoot ) {
        StoreCleanupHandle(hRoot, 0, TRUE);
    }

    hRoot = &hStore1;
    Err = StoreInitHandle(
        hRoot,
        0,
        wcslen(gDsServer)?gDsServer:NULL,
        NULL,
        NULL,
        NULL,
        0
    );
    if( ERROR_SUCCESS != Err ) {
        printf("StoreInitHandle():%ld 0x%lx\n", Err, Err);
        hRoot = NULL;
        return ;
    }

    if( hDhcpC ) StoreCleanupHandle(hDhcpC, 0, TRUE);
    hDhcpC = &hStore2;

    Err = StoreGetHandle(
        hRoot,
        0,
        StoreGetChildType,
        DHCP_ROOT_OBJECT_PARENT_LOC,
        hDhcpC
    );
    if( ERROR_SUCCESS != Err ) {
        printf("StoreInitHandle():%ld 0x%lx\n", Err, Err);
        hRoot = NULL;
        return ;
    }

    if( hDhcpRoot ) StoreCleanupHandle(hDhcpRoot, 0, TRUE);
    hDhcpRoot = &hStore4;
    Err = DhcpDsGetRoot(
        DDS_FLAGS_CREATE,
        hRoot,
        hDhcpRoot
    );

    if( ERROR_SUCCESS != Err) {
        printf("DhcpDsGetRoot(): %ld 0x%lx", Err, Err);
        hDhcpRoot = NULL;
    }
}

VOID
SetObject(
    IN      LPWSTR                 VarNameUnused
)
{
}

VOID
CreateOptDef(
    IN      LPWSTR                 VarNameUnused
)
{
    DWORD                          Result;

    Result = DhcpDsCreateOptionDef(
        hDhcpC,
        hObject,
        0,
        gOptName,
        wcslen(gOptComment)?gOptComment:NULL,
        wcslen(gClassName)?gClassName: NULL,
        gOptId,
        gOptType,
        gOptVal,
        gOptLen
    );

    printf("DhcpDsCreateOptionDef: %ld (0x%lx)\n", Result, Result);
}

VOID
ModifyOptDef(
    IN      LPWSTR                 VarNameUnused
)
{
    DWORD                          Result;

    Result = DhcpDsModifyOptionDef(
        hDhcpC,
        hObject,
        0,
        gOptName,
        wcslen(gOptComment)?gOptComment:NULL,
        wcslen(gClassName)?gClassName: NULL,
        gOptId,
        gOptType,
        gOptVal,
        gOptLen
    );

    printf("DhcpDsModifyOptionDef: %ld (0x%lx)\n", Result, Result);
}

VOID
DeleteOptDef(
    IN      LPWSTR                 VarNameUnused
)
{
    DWORD                          Result;

    Result = DhcpDsDeleteOptionDef(
        hDhcpC,
        hObject,
        0,
        wcslen(gClassName)?gClassName: NULL,
        gOptId
    );

    printf("DhcpDsDeleteOptionDef: %ld (0x%lx)\n", Result, Result);
}

VOID
EnumOptDefs(
    IN      LPWSTR                 VarNameUnused
)
{
    DWORD                          Result;
    LPDHCP_OPTION_ARRAY            OptArray = NULL;

    Result = DhcpDsEnumOptionDefs(
        hDhcpC,
        hObject,
        0,
        wcslen(gClassName)?gClassName: NULL,
        gIsVendor,
        &OptArray
    );

    printf("DhcpDsEnumOptionDefs: %ld (0x%lx)\n", Result, Result);
    if( ERROR_SUCCESS == Result ) print_DHCP_OPTION_ARRAY(0, OptArray);
    if( NULL != OptArray ) LocalFree(OptArray);
}

VOID
SetOptValue(
    IN      LPWSTR                 VarNameUnused
)
{
    DWORD                          Result;
    DHCP_OPTION_DATA               OptData;
    DHCP_OPTION_DATA_ELEMENT       OptDataElement;

    OptData.NumElements = 1;
    OptData.Elements = &OptDataElement;
    OptDataElement.OptionType = DhcpDWordOption;
    OptDataElement.Element.DWordOption = gDwordOptData;

    Result = DhcpDsSetOptionValue(
        hDhcpC,
        hObject,
        0,
        wcslen(gClassName)?gClassName: NULL,
        gOptId,
        &OptData
    );

    printf("DhcpDsSetOptionValue: %ld (0x%lx)\n", Result, Result);
}

VOID
RemoveOptValue(
    IN      LPWSTR                 VarNameUnused
)
{
    DWORD                          Result;

    Result = DhcpDsRemoveOptionValue(
        hDhcpC,
        hObject,
        0,
        wcslen(gClassName)? gClassName : NULL,
        gOptId
    );

    printf("DhcpDsRemoveOptionValue: %ld (0x%lx)\n", Result, Result);
}

VOID
GetOptValue(
    IN      LPWSTR                 VarNameUnused
)
{
    DWORD                          Result;
    LPDHCP_OPTION_VALUE            OptVal = NULL;

    Result = DhcpDsGetOptionValue(
        hDhcpC,
        hObject,
        0,
        wcslen(gClassName)? gClassName : NULL,
        gOptId,
        &OptVal
    );

    printf("DhcpDsGetOptionValue: %ld (0x%lx)\n", Result, Result);
    if( ERROR_SUCCESS == Result ) print_DHCP_OPTION_VALUE(0, OptVal);
    if( OptVal ) LocalFree(OptVal);
}

VOID
EnumOptValues(
    IN      LPWSTR                 VarNameUnused
)
{
    DWORD                          Result;
    LPDHCP_OPTION_VALUE_ARRAY      OptValues = NULL;

    Result = DhcpDsEnumOptionValues(
        hDhcpC,
        hObject,
        0,
        wcslen(gClassName)? gClassName : NULL,
        gIsVendor,
        &OptValues
    );

    printf("DhcpDsEnumOptionValue: %ld (0x%lx)\n", Result, Result);
    if( ERROR_SUCCESS == Result ) print_DHCP_OPTION_VALUE_ARRAY(0, OptValues);
    if( OptValues ) LocalFree(OptValues);
}

VOID
CreateClass(
    IN      LPWSTR                 VarNameUnused
)
{
    DWORD                          Result;

    Result = DhcpDsCreateClass(
        hDhcpC,
        hObject,
        0,
        wcslen(gClassName)? gClassName : NULL,
        wcslen(gClassComment)? gClassComment : NULL,
        gClassData,
        gClassDataLen
    );
    printf("DhcpDsCreateClass: %ld (0x%lx)\n", Result, Result);
}

VOID
DeleteClass(
    IN      LPWSTR                 VarNameUnused
)
{
    DWORD                          Result;

    Result = DhcpDsDeleteClass(
        hDhcpC,
        hObject,
        0,
        gClassName
    );
    printf("DhcpDsDeleteClass: %ld (0x%lx)\n", Result, Result);
}

VOID
EnumClasses(
    IN      LPWSTR                 VarNameUnused
)
{
    DWORD                          Result;
    LPDHCP_CLASS_INFO_ARRAY        Classes = NULL;

    Result = DhcpDsEnumClasses(
        hDhcpC,
        hObject,
        0,
        &Classes
    );

    printf("DhcpDsEnumClasses: %ld (0x%lx)\n", Result, Result);
    if( ERROR_SUCCESS == Result ) print_DHCP_CLASS_INFO_ARRAY(0, Classes);
    if( Classes ) LocalFree(Classes);
}

VOID
AddServer(
    IN      LPWSTR                 VarNameUnused
)
{
    DWORD                          Result;

    Result = DhcpDsAddServer(
        hDhcpC,
        hDhcpRoot,
        0,
        gServerName,
        NULL,
        gServerAddress,
        gServerState
    );

    printf("DhcpDsAddServer: %ld (0x%lx)\n", Result, Result);
}

VOID
DelServer(
    IN      LPWSTR                 VarNameUnused
)
{
    DWORD                          Result;

    Result = DhcpDsDelServer(
        hDhcpC,
        hDhcpRoot,
        0,
        gServerName,
        NULL,
        gServerAddress
    );

    printf("DhcpDsDelServer: %ld (0x%lx)\n", Result, Result);
}

VOID
EnumServers(
    IN      LPWSTR                 VarNameUnused
)
{
    DWORD                          Result;
    LPDHCPDS_SERVERS               Servers;

    Servers = NULL;
    Result = DhcpDsEnumServers(
        hDhcpC,
        hDhcpRoot,
        0,
        &Servers
    );

    printf("DhcpDsEnumServer: %ld (0x%lx)\n", Result, Result);
    if( ERROR_SUCCESS == Result ) print_DHCPDS_SERVERS( 0, Servers);
    if( Servers ) MemFree(Servers);
}


NJIN_VAR VarTable[] = {
    L"DsServer",   wString, { (LPVOID) &gDsServer }, SetDsServer, FALSE,   //0
    L"Object",     wString, { (LPVOID) &gObject }, SetObject,  FALSE,      //1
    L"OptName",    wString, { (LPVOID) &gOptName }, NULL, FALSE,           //2
    L"OptComment", wString, { (LPVOID) &gOptComment }, NULL, FALSE,        //3
    L"ClassName",  wString, { (LPVOID) &gClassName }, NULL, FALSE,         //4
    L"ClassComment",wString,{ (LPVOID) &gClassComment }, NULL, FALSE,      //5
    L"OptId",      DWord, { (LPVOID) &gOptId }, NULL, FALSE,               //6
    L"OptType",    DWord, { (LPVOID) &gOptType }, NULL, FALSE,             //7
    L"OptData",    xBytes, { (LPVOID) NULL }, NULL, FALSE, //############ -> Need to fill pointer..
    L"IsVendor",   DWord, { (LPVOID) &gIsVendor }, NULL, FALSE,            //9
    L"DwordOptData", DWord, { (LPVOID) &gDwordOptData }, NULL, FALSE,      //10
    L"ClassData",  xBytes, { (LPVOID) NULL }, NULL, FALSE, //############ -> need to fill pointer
    L"ServerName", wString, { (LPVOID) &gServerName }, NULL, FALSE,
    L"ServerAddress", IpAddress, { (LPVOID) &gServerAddress }, NULL, FALSE,
    L"ServerState", DWord, { (LPVOID) &gServerState }, NULL, FALSE,

    L"CreateOptionDef", wString, { (LPVOID) &Dummy }, CreateOptDef, FALSE, //12
    L"ModifyOptionDef", wString, { (LPVOID) &Dummy }, ModifyOptDef, FALSE,
    L"EnumOptionDefs",  wString, { (LPVOID) &Dummy }, EnumOptDefs, FALSE,
    L"SetOptionValue",  wString, { (LPVOID) &Dummy }, SetOptValue, FALSE,
    L"RemoveOptionValue", wString, { (LPVOID) &Dummy }, RemoveOptValue, FALSE,
    L"GetOptionValue",    wString, { (LPVOID) &Dummy }, GetOptValue, FALSE,
    L"EnumOptionValues",  wString, { (LPVOID) &Dummy }, EnumOptValues, FALSE,
    L"CreateClass",       wString, { (LPVOID) &Dummy }, CreateClass, FALSE,
    L"DeleteClass",       wString, { (LPVOID) &Dummy }, DeleteClass, FALSE,
    L"EnumClasses",       wString, { (LPVOID) &Dummy }, EnumClasses, FALSE,
    L"AddServer",         wString, { (LPVOID) &Dummy }, AddServer, FALSE,
    L"DelServer",         wString, { (LPVOID) &Dummy }, DelServer, FALSE,
    L"EnumServers",       wString, { (LPVOID) &Dummy }, EnumServers, FALSE,
};

NJIN_ENV Env[] = {
    L'#',
    L'=',
    L'\r',
    L'\n',
    L' ',
    L'#',
    TRUE,
    TRUE
};

#define  VarTableSz                (sizeof(VarTable)/sizeof(VarTable[0]))

void _cdecl main (void) {
    DWORD                          Result;
    DWORD                          i;

    VarTable[8].VarData.xBytesLen = &gOptLen;
    VarTable[8].VarData.xBytesBuf = gOptVal;
    VarTable[11].VarData.xBytesLen = &gClassDataLen;
    VarTable[11].VarData.xBytesBuf = gClassData;

    Result = NjinExecute(
        Env,
        VarTable,
        VarTableSz
    );

    if( ERROR_SUCCESS != Result ) {
        printf("\nResult = %ld\n", Result);
    }
}


//================================================================================
// end of file
//================================================================================