649 lines
18 KiB
C
649 lines
18 KiB
C
//================================================================================
|
|
// Copyright (C) 1997 Microsoft Corporation
|
|
// Author: RameshV
|
|
// Description: test utilility for options api
|
|
//================================================================================
|
|
|
|
#include <windows.h>
|
|
#include <winsock.h>
|
|
#include <dhcp.h>
|
|
#include <dhcpapi.h>
|
|
#include <dhcplib.h>
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <wchar.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
#include <jet.h> // for JET_cbColumnMost
|
|
|
|
LPWSTR GlobalServerName = L"127.0.0.1" ;
|
|
const DWORD ReservedZero = 0;
|
|
const DWORD PreferredMax = 0xFFFFFFF;
|
|
DHCP_CLASS_INFO DhcpGlobalClassInfo;
|
|
|
|
//================================================================================
|
|
// utilties
|
|
//================================================================================
|
|
LPWSTR
|
|
WSTRING(
|
|
IN LPSTR String
|
|
)
|
|
{
|
|
LPWSTR WString, Tmp;
|
|
|
|
WString = DhcpAllocateMemory(sizeof(WCHAR)*(strlen(String)+1));
|
|
if( NULL == WString ) return NULL;
|
|
Tmp = WString;
|
|
|
|
while(*Tmp++ = (WCHAR)*String++);
|
|
return WString;
|
|
}
|
|
|
|
VOID
|
|
DhcpPrintRoutine(
|
|
IN DWORD DebugFlag,
|
|
IN LPSTR Format,
|
|
...
|
|
)
|
|
|
|
{
|
|
|
|
#define WSTRSIZE( wsz ) ( ( wcslen( wsz ) + 1 ) * sizeof( WCHAR ) )
|
|
|
|
#define MAX_PRINTF_LEN 1024 // Arbitrary.
|
|
|
|
va_list arglist;
|
|
char OutputBuffer[MAX_PRINTF_LEN];
|
|
ULONG length = 0;
|
|
|
|
//
|
|
// Put a the information requested by the caller onto the line
|
|
//
|
|
|
|
va_start(arglist, Format);
|
|
length += (ULONG) vsprintf(&OutputBuffer[length], Format, arglist);
|
|
va_end(arglist);
|
|
|
|
DhcpAssert(length <= MAX_PRINTF_LEN);
|
|
|
|
//
|
|
// Output to the debug terminal,
|
|
//
|
|
|
|
printf( "%s", OutputBuffer);
|
|
}
|
|
|
|
//================================================================================
|
|
// the main code for each function follows
|
|
//================================================================================
|
|
|
|
DWORD
|
|
CreateClass(
|
|
IN DWORD nAargs,
|
|
IN LPSTR *Args
|
|
)
|
|
{
|
|
DWORD Error;
|
|
LPDHCP_CLASS_INFO ClassInfo = &DhcpGlobalClassInfo;
|
|
|
|
ClassInfo->ClassName = WSTRING(Args[0]);
|
|
ClassInfo->ClassComment = NULL;
|
|
ClassInfo->ClassDataLength = strlen(Args[1]);
|
|
ClassInfo->ClassData = Args[1];
|
|
Error = DhcpCreateClass(
|
|
GlobalServerName,
|
|
ReservedZero,
|
|
ClassInfo
|
|
);
|
|
DhcpFreeMemory(ClassInfo->ClassName);
|
|
|
|
printf("Status = %ld\n", Error);
|
|
return Error;
|
|
}
|
|
|
|
DWORD
|
|
ModifyClass(
|
|
IN DWORD nAargs,
|
|
IN LPSTR *Args
|
|
)
|
|
{
|
|
DWORD Error;
|
|
LPDHCP_CLASS_INFO ClassInfo = &DhcpGlobalClassInfo;
|
|
|
|
ClassInfo->ClassName = WSTRING(Args[0]);
|
|
ClassInfo->ClassComment = NULL;
|
|
ClassInfo->ClassDataLength = strlen(Args[1]);
|
|
ClassInfo->ClassData = Args[1];
|
|
Error = DhcpModifyClass(
|
|
GlobalServerName,
|
|
ReservedZero,
|
|
ClassInfo
|
|
);
|
|
DhcpFreeMemory(ClassInfo->ClassName);
|
|
|
|
printf("Status = %ld\n", Error);
|
|
return Error;
|
|
}
|
|
|
|
DWORD
|
|
DeleteClass(
|
|
IN DWORD nArgs,
|
|
IN LPSTR *Args
|
|
)
|
|
{
|
|
DWORD Error;
|
|
LPWSTR ClassName;
|
|
|
|
ClassName = WSTRING(Args[0]);
|
|
Error = DhcpDeleteClass(
|
|
GlobalServerName,
|
|
ReservedZero,
|
|
ClassName
|
|
);
|
|
DhcpFreeMemory(ClassName);
|
|
|
|
printf("Status = %ld\n", Error);
|
|
return Error;
|
|
}
|
|
|
|
VOID
|
|
PrintClass(
|
|
IN LPDHCP_CLASS_INFO Class
|
|
)
|
|
{
|
|
DWORD Index;
|
|
printf("%S (%S) [%d: ", Class->ClassName, Class->ClassComment, Class->ClassDataLength);
|
|
for( Index = 0; Index < Class->ClassDataLength; Index ++ )
|
|
printf("%02x ", Class->ClassData[Index]);
|
|
printf("\n");
|
|
}
|
|
|
|
DWORD
|
|
GetClassInfoX(
|
|
IN DWORD nArgs,
|
|
IN LPSTR *Args
|
|
)
|
|
{
|
|
DWORD Error;
|
|
LPDHCP_CLASS_INFO ClassInfo = &DhcpGlobalClassInfo, OutClassInfo;
|
|
LPWSTR ClassName;
|
|
|
|
memset(ClassInfo, 0, sizeof(*ClassInfo) );
|
|
ClassInfo->ClassName = WSTRING(Args[0]);
|
|
OutClassInfo = NULL;
|
|
Error = DhcpGetClassInfo(
|
|
GlobalServerName,
|
|
ReservedZero,
|
|
ClassInfo,
|
|
&OutClassInfo
|
|
);
|
|
DhcpFreeMemory(ClassInfo->ClassName);
|
|
if( ERROR_SUCCESS == Error ) {
|
|
PrintClass(OutClassInfo);
|
|
DhcpRpcFreeMemory(OutClassInfo);
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
printf("Status = %ld\n", Error);
|
|
return Error;
|
|
}
|
|
|
|
DWORD
|
|
EnumClasses(
|
|
IN DWORD nArgs,
|
|
IN LPSTR *Args
|
|
)
|
|
{
|
|
DWORD Error;
|
|
DWORD Index;
|
|
DWORD nRead;
|
|
DWORD nTotal;
|
|
LPDHCP_CLASS_INFO_ARRAY OutClasses;
|
|
DHCP_RESUME_HANDLE Resume = 0;
|
|
|
|
OutClasses = NULL;
|
|
Error = DhcpEnumClasses(
|
|
GlobalServerName,
|
|
ReservedZero,
|
|
&Resume,
|
|
PreferredMax,
|
|
&OutClasses,
|
|
&nRead,
|
|
&nTotal
|
|
);
|
|
|
|
if( ERROR_SUCCESS == Error ) {
|
|
printf("nRead = %ld nTotal = %ld NumElements = %ld\n",
|
|
nRead, nTotal, OutClasses?OutClasses->NumElements:0
|
|
);
|
|
for( Index = 0; Index < nRead; Index ++ ) {
|
|
PrintClass(&OutClasses->Classes[Index]);
|
|
}
|
|
if( OutClasses) DhcpRpcFreeMemory(OutClasses);
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
printf("Status = %ld\n", Error);
|
|
return Error;
|
|
}
|
|
|
|
DWORD
|
|
CreateOption(
|
|
IN DWORD nArgs,
|
|
IN LPSTR *Args
|
|
)
|
|
{
|
|
DWORD Error;
|
|
DWORD OptionId;
|
|
DWORD OptionData;
|
|
LPWSTR VendorName;
|
|
LPWSTR OptName;
|
|
LPWSTR OptComment;
|
|
BOOL IsVendor;
|
|
DHCP_OPTION OptInfo;
|
|
DHCP_OPTION_DATA_ELEMENT DhcpOptionDataElement;
|
|
|
|
OptionId = atoi(Args[0]);
|
|
VendorName = (0 == _stricmp(Args[1], "NULL"))?NULL:WSTRING(Args[1]);
|
|
IsVendor = (0 == _stricmp(Args[2], "IsVendor"));
|
|
OptName = WSTRING(Args[3]);
|
|
OptComment = WSTRING(Args[4]);
|
|
OptionData = atoi(Args[5]);
|
|
|
|
OptInfo.OptionID = OptionId;
|
|
OptInfo.OptionName = OptName;
|
|
OptInfo.OptionComment = OptComment;
|
|
OptInfo.OptionType = DhcpDWordOption;
|
|
OptInfo.DefaultValue.NumElements = 1;
|
|
OptInfo.DefaultValue.Elements = &DhcpOptionDataElement;
|
|
DhcpOptionDataElement.Element.DWordOption = OptionData;
|
|
DhcpOptionDataElement.OptionType = DhcpDWordOption;
|
|
|
|
Error = DhcpCreateOptionV5(
|
|
GlobalServerName,
|
|
IsVendor?DHCP_FLAGS_OPTION_IS_VENDOR:0,
|
|
OptionId,
|
|
NULL,
|
|
VendorName,
|
|
&OptInfo
|
|
);
|
|
|
|
printf("Status = %ld\n", Error);
|
|
return Error;
|
|
}
|
|
|
|
DWORD
|
|
SetOptionInfo(
|
|
IN DWORD nArgs,
|
|
IN LPSTR *Args
|
|
)
|
|
{
|
|
DWORD Error;
|
|
DWORD OptionId;
|
|
DWORD OptionData;
|
|
LPWSTR ClassName;
|
|
LPWSTR VendorName;
|
|
LPWSTR OptName;
|
|
LPWSTR OptComment;
|
|
BOOL IsVendor;
|
|
DHCP_OPTION OptInfo;
|
|
DHCP_OPTION_DATA_ELEMENT DhcpOptionDataElement;
|
|
|
|
OptionId = atoi(Args[0]);
|
|
ClassName = (0 == _stricmp(Args[1], "NULL"))?NULL:WSTRING(Args[1]);
|
|
IsVendor = (0 == _stricmp(Args[2], "IsVendor"));
|
|
OptName = WSTRING(Args[3]);
|
|
OptComment = WSTRING(Args[4]);
|
|
OptionData = atoi(Args[5]);
|
|
|
|
OptInfo.OptionID = OptionId;
|
|
OptInfo.OptionName = OptName;
|
|
OptInfo.OptionComment = OptComment;
|
|
OptInfo.OptionType = DhcpDWordOption;
|
|
OptInfo.DefaultValue.NumElements = 1;
|
|
OptInfo.DefaultValue.Elements = &DhcpOptionDataElement;
|
|
DhcpOptionDataElement.Element.DWordOption = OptionData;
|
|
DhcpOptionDataElement.OptionType = DhcpDWordOption;
|
|
|
|
Error = DhcpSetOptionInfoV5(
|
|
GlobalServerName,
|
|
OptionId,
|
|
ClassName,
|
|
IsVendor,
|
|
&OptInfo
|
|
);
|
|
|
|
printf("Status = %ld\n", Error);
|
|
return Error;
|
|
}
|
|
|
|
VOID
|
|
PrintOptionInfo(
|
|
IN LPDHCP_OPTION Option
|
|
)
|
|
{
|
|
printf("%03ld %S (%S) Type = %ld\n",
|
|
Option->OptionID, Option->OptionName, Option->OptionComment, Option->OptionType
|
|
);
|
|
}
|
|
|
|
DWORD
|
|
GetOptionInfo(
|
|
IN DWORD nArgs,
|
|
IN LPSTR *Args
|
|
)
|
|
{
|
|
DWORD Error;
|
|
DWORD OptionId;
|
|
DWORD OptionData;
|
|
LPWSTR ClassName;
|
|
LPWSTR OptName;
|
|
LPWSTR OptComment;
|
|
BOOL IsVendor;
|
|
LPDHCP_OPTION OptInfo;
|
|
DHCP_OPTION_DATA_ELEMENT DhcpOptionDataElement;
|
|
|
|
OptionId = atoi(Args[0]);
|
|
ClassName = (0 == _stricmp(Args[1], "NULL"))?NULL:WSTRING(Args[1]);
|
|
IsVendor = (0 == _stricmp(Args[2], "IsVendor"));
|
|
|
|
OptInfo = NULL;
|
|
Error = DhcpGetOptionInfoV5(
|
|
GlobalServerName,
|
|
OptionId,
|
|
ClassName,
|
|
IsVendor,
|
|
&OptInfo
|
|
);
|
|
|
|
if( ERROR_SUCCESS == Error ) {
|
|
PrintOptionInfo(OptInfo);
|
|
DhcpRpcFreeMemory(OptInfo);
|
|
return ERROR_SUCCESS;
|
|
}
|
|
printf("Status = %ld\n", Error);
|
|
return Error;
|
|
}
|
|
|
|
DWORD
|
|
RemoveOption(
|
|
IN DWORD nArgs,
|
|
IN LPSTR *Args
|
|
)
|
|
{
|
|
DWORD Error;
|
|
DWORD OptionId;
|
|
DWORD OptionData;
|
|
LPWSTR ClassName;
|
|
LPWSTR OptName;
|
|
LPWSTR OptComment;
|
|
BOOL IsVendor;
|
|
|
|
OptionId = atoi(Args[0]);
|
|
ClassName = (0 == _stricmp(Args[1], "NULL"))?NULL:WSTRING(Args[1]);
|
|
IsVendor = (0 == _stricmp(Args[2], "IsVendor"));
|
|
|
|
Error = DhcpRemoveOptionV5(
|
|
GlobalServerName,
|
|
OptionId,
|
|
ClassName,
|
|
IsVendor
|
|
);
|
|
|
|
printf("Status = %ld\n", Error);
|
|
return Error;
|
|
}
|
|
|
|
DWORD
|
|
SetOptionValue(
|
|
IN DWORD nArgs,
|
|
IN LPSTR *Args
|
|
)
|
|
{
|
|
DWORD Error;
|
|
DWORD OptionId;
|
|
DWORD OptionData;
|
|
LPWSTR ClassName;
|
|
BOOL IsVendor;
|
|
DHCP_OPTION_SCOPE_INFO ScopeInfo;
|
|
DHCP_OPTION_DATA OptData;
|
|
DHCP_OPTION_DATA_ELEMENT OptDataElement;
|
|
|
|
OptionId = atoi(Args[0]);
|
|
ClassName = (0 == _stricmp(Args[1], "NULL"))?NULL:WSTRING(Args[1]);
|
|
IsVendor = (0 == _stricmp(Args[2], "IsVendor"));
|
|
OptionData = atoi(Args[3]);
|
|
|
|
OptData.NumElements = 1;
|
|
OptData.Elements = &OptDataElement;
|
|
OptDataElement.OptionType = DhcpDWordOption;
|
|
OptDataElement.Element.DWordOption = OptionData;
|
|
ScopeInfo.ScopeType = DhcpGlobalOptions;
|
|
|
|
Error = DhcpSetOptionValueV5(
|
|
GlobalServerName,
|
|
OptionId,
|
|
ClassName,
|
|
IsVendor,
|
|
&ScopeInfo,
|
|
&OptData
|
|
);
|
|
|
|
printf("Status = %ld\n", Error);
|
|
return Error;
|
|
}
|
|
|
|
DWORD
|
|
GetOptionValue(
|
|
IN DWORD nArgs,
|
|
IN LPSTR *Args
|
|
)
|
|
{
|
|
DWORD Error;
|
|
DWORD OptionId;
|
|
DWORD OptionData;
|
|
LPWSTR ClassName;
|
|
BOOL IsVendor;
|
|
DHCP_OPTION_SCOPE_INFO ScopeInfo;
|
|
LPDHCP_OPTION_VALUE OptData;
|
|
|
|
OptionId = atoi(Args[0]);
|
|
ClassName = (0 == _stricmp(Args[1], "NULL"))?NULL:WSTRING(Args[1]);
|
|
IsVendor = (0 == _stricmp(Args[2], "IsVendor"));
|
|
|
|
ScopeInfo.ScopeType = DhcpGlobalOptions;
|
|
OptData = NULL;
|
|
Error = DhcpGetOptionValueV5(
|
|
GlobalServerName,
|
|
OptionId,
|
|
ClassName,
|
|
IsVendor,
|
|
&ScopeInfo,
|
|
&OptData
|
|
);
|
|
|
|
if( ERROR_SUCCESS == Error ) {
|
|
printf("OptionId =%ld NumElements = %ld, Type = %ld\n Data = %ld", OptData->Value.NumElements,
|
|
OptData->OptionID,
|
|
OptData->Value.NumElements?OptData->Value.Elements[0].OptionType:-1,
|
|
OptData->Value.NumElements?OptData->Value.Elements[0].Element.DWordOption:-1
|
|
);
|
|
DhcpRpcFreeMemory(OptData);
|
|
return ERROR_SUCCESS;
|
|
}
|
|
printf("Status = %ld\n", Error);
|
|
return Error;
|
|
}
|
|
|
|
VOID
|
|
PrintOptionArray(
|
|
IN LPDHCP_OPTION_ARRAY Options
|
|
)
|
|
{
|
|
DWORD Index;
|
|
|
|
if( NULL == Options ) return;
|
|
|
|
for(Index = 0; Index < Options->NumElements; Index ++ )
|
|
PrintOptionInfo(&Options->Options[Index]);
|
|
}
|
|
|
|
DWORD
|
|
GetAllOptions(
|
|
IN DWORD nArgs,
|
|
IN LPSTR *Args
|
|
)
|
|
{
|
|
DWORD Error;
|
|
LPDHCP_ALL_OPTIONS AllOpts;
|
|
|
|
AllOpts = NULL;
|
|
Error = DhcpGetAllOptions(
|
|
GlobalServerName,
|
|
DHCP_OPT_ENUM_IGNORE_VENDOR,
|
|
FALSE,
|
|
NULL,
|
|
&AllOpts
|
|
);
|
|
|
|
if( ERROR_SUCCESS == Error ) {
|
|
printf("VendorOptions:\n");
|
|
PrintOptionArray(AllOpts->VendorOptions);
|
|
printf("NonVendorOptions:\n");
|
|
PrintOptionArray(AllOpts->NonVendorOptions);
|
|
DhcpRpcFreeMemory(AllOpts);
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
printf("Status = %ld\n", Error);
|
|
return Error;
|
|
}
|
|
|
|
VOID
|
|
PrintOptionValueArray(
|
|
IN LPDHCP_OPTION_VALUE_ARRAY Array
|
|
)
|
|
{
|
|
DWORD Index;
|
|
|
|
if( NULL == Array ) return;
|
|
|
|
for(Index = 0; Index < Array->NumElements; Index ++ )
|
|
printf("OptionId = %ld NumElements = %ld Type = %ld Data = %ld\n",
|
|
Array->Values[Index].OptionID, Array->Values[Index].Value.NumElements,
|
|
Array->Values[Index].Value.Elements[0].OptionType,
|
|
Array->Values[Index].Value.Elements[0].Element.DWordOption
|
|
);
|
|
|
|
}
|
|
|
|
DWORD
|
|
GetAllOptionValues(
|
|
IN DWORD nArgs,
|
|
IN LPSTR *Args
|
|
)
|
|
{
|
|
LPDHCP_ALL_OPTION_VALUES OptionValues;
|
|
DHCP_OPTION_SCOPE_INFO ScopeInfo;
|
|
DWORD Error;
|
|
DWORD Index;
|
|
|
|
OptionValues = NULL;
|
|
ScopeInfo.ScopeType = DhcpGlobalOptions;
|
|
Error = DhcpGetAllOptionValues(
|
|
GlobalServerName,
|
|
DHCP_OPT_ENUM_IGNORE_VENDOR,
|
|
FALSE,
|
|
NULL,
|
|
&ScopeInfo,
|
|
&OptionValues
|
|
);
|
|
|
|
if( ERROR_SUCCESS == Error ) {
|
|
printf("Default Vendor option values:\n");
|
|
PrintOptionValueArray(OptionValues->DefaultValues.VendorOptions);
|
|
printf("Default NonVendor option values:\n");
|
|
PrintOptionValueArray(OptionValues->DefaultValues.NonVendorOptions);
|
|
|
|
if( OptionValues->ClassInfoArray )
|
|
for(Index = 0; Index < OptionValues->ClassInfoArray->NumElements ; Index ++ ) {
|
|
printf("Class %ws : \n", OptionValues->ClassInfoArray->Classes[Index].ClassName);
|
|
PrintOptionValueArray(OptionValues->VendorOptionValueForClassArray.Elements[Index]);
|
|
PrintOptionValueArray(OptionValues->NonVendorOptionValueForClassArray.Elements[Index]);
|
|
}
|
|
DhcpRpcFreeMemory(OptionValues);
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
printf("Status = %ld\n", Error);
|
|
return Error;
|
|
}
|
|
|
|
struct {
|
|
LPSTR CmdName;
|
|
DWORD MinArgs;
|
|
DWORD (*Func)(IN DWORD nArgs, IN LPSTR *Args);
|
|
LPWSTR Comment;
|
|
} Table[] = {
|
|
"CreateClass", 3, CreateClass, L"CreateClass ClassName AsciiStringForClass\n",
|
|
"ModifyClass", 3, ModifyClass, L"ModifyClass ClassName AsciiStringForClass\n",
|
|
"DeleteClass", 3, DeleteClass, L"DeleteClass ClassName\n",
|
|
"GetClassInfo", 3, GetClassInfoX, L"GetClassInfo ClassName\n",
|
|
"EnumClasses", 2, EnumClasses, L"EnumClassses\n",
|
|
"CreateOption", 3, CreateOption, L"CreateOption OptionId(INT) ClassName \"Is(Not)Vendor\" OptName "
|
|
L"OptionComment OptionData(INT)\n",
|
|
"SetOptionInfo", 3, SetOptionInfo, L"SetOptionInfo OptionId(INT) ClassName \"Is(Not)Vendor\" OptName "
|
|
L"OptionComment OptionData(INT)\n",
|
|
"GetOptionInfo", 3, GetOptionInfo, L"GetOptionInfo OptionId(INT) ClassName \"Is(Not)Vendor\" \n",
|
|
"RemoveOption", 3, RemoveOption, L"RemoveOption OptionId(INT) ClassName \"Is(Not)Vendor\" \n",
|
|
"SetOptionValue",3, SetOptionValue, L"SetOptionValue OptionId(INT) ClassName \"Is(Not)Vendor\" OptData(INT)\n",
|
|
"GetOptionValue",3, GetOptionValue, L"GetOptionValue OptionId(INT) ClassName \"Is(Not)Vendor\"\n",
|
|
"GetAllOptions", 2, GetAllOptions, L"GetAllOptions\n",
|
|
"GetAllOptionValues", 2, GetAllOptionValues, L"GetAllOptionValues\n",
|
|
};
|
|
|
|
DWORD
|
|
PrintUsage(
|
|
VOID
|
|
)
|
|
{
|
|
DWORD Index;
|
|
|
|
for( Index = 0; Index < sizeof(Table)/sizeof(Table[0]) ; Index ++ )
|
|
printf("%S", Table[Index].Comment);
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
DWORD
|
|
Main(
|
|
IN DWORD nArgs,
|
|
IN LPSTR *Args
|
|
)
|
|
{
|
|
DWORD Index;
|
|
|
|
if( 1 >= nArgs ) return PrintUsage();
|
|
|
|
for( Index = 0; Index < sizeof(Table)/sizeof(Table[0]); Index ++ ) {
|
|
if( 0 == _stricmp(Table[Index].CmdName, Args[1]) )
|
|
if( nArgs < Table[Index].MinArgs ) {
|
|
printf("%S", Table[Index].Comment);
|
|
return ERROR_SUCCESS;
|
|
} else return Table[Index].Func(nArgs-2, Args+2);
|
|
}
|
|
|
|
return PrintUsage();
|
|
}
|
|
|
|
void _cdecl main( int argc, char *argv[] )
|
|
{
|
|
Main(argc, argv);
|
|
}
|
|
|
|
|
|
//================================================================================
|
|
// end of file
|
|
//================================================================================
|