You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1991 lines
48 KiB
1991 lines
48 KiB
/* Copyright (c) 1995, Microsoft Corporation, all rights reserved
|
|
**
|
|
** noui.c
|
|
** Non-UI helper routines (no HWNDs required)
|
|
** Listed alphabetically
|
|
**
|
|
** 08/25/95 Steve Cobb
|
|
*/
|
|
|
|
#include <windows.h> // Win32 root
|
|
#include <stdlib.h> // for atol()
|
|
#include <nouiutil.h> // Our public header
|
|
#include <debug.h> // Trace/Assert library
|
|
#include <nnetcfg.h>
|
|
|
|
WCHAR*
|
|
StrDupWFromAInternal(
|
|
LPCSTR psz,
|
|
UINT uiCodePage);
|
|
|
|
INT
|
|
ComparePszNode(
|
|
IN DTLNODE* pNode1,
|
|
IN DTLNODE* pNode2 )
|
|
|
|
/* Callback for DtlMergeSort; takes two DTLNODE*'s whose data
|
|
** are assumed to be strings (TCHAR*), and compares the strings.
|
|
**
|
|
** Return value is as defined for 'lstrcmpi'.
|
|
*/
|
|
{
|
|
return lstrcmpi( (TCHAR *)DtlGetData(pNode1), (TCHAR *)DtlGetData(pNode2) );
|
|
}
|
|
|
|
|
|
DWORD
|
|
CreateDirectoriesOnPath(
|
|
LPTSTR pszPath,
|
|
LPSECURITY_ATTRIBUTES psa)
|
|
{
|
|
DWORD dwErr = ERROR_SUCCESS;
|
|
|
|
if (pszPath && *pszPath)
|
|
{
|
|
LPTSTR pch = pszPath;
|
|
|
|
// If the path is a UNC path, we need to skip the \\server\share
|
|
// portion.
|
|
//
|
|
if ((TEXT('\\') == *pch) && (TEXT('\\') == *(pch+1)))
|
|
{
|
|
// pch now pointing at the server name. Skip to the backslash
|
|
// before the share name.
|
|
//
|
|
pch += 2;
|
|
while (*pch && (TEXT('\\') != *pch))
|
|
{
|
|
pch++;
|
|
}
|
|
|
|
if (!*pch)
|
|
{
|
|
// Just the \\server was specified. This is bogus.
|
|
//
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
// pch now pointing at the backslash before the share name.
|
|
// Skip to the backslash that should come after the share name.
|
|
//
|
|
pch++;
|
|
while (*pch && (TEXT('\\') != *pch))
|
|
{
|
|
pch++;
|
|
}
|
|
|
|
if (!*pch)
|
|
{
|
|
// Just the \\server\share was specified. No subdirectories
|
|
// to create.
|
|
//
|
|
return ERROR_SUCCESS;
|
|
}
|
|
}
|
|
|
|
// Loop through the path.
|
|
//
|
|
for (; *pch; pch++)
|
|
{
|
|
// Stop at each backslash and make sure the path
|
|
// is created to that point. Do this by changing the
|
|
// backslash to a null-terminator, calling CreateDirecotry,
|
|
// and changing it back.
|
|
//
|
|
if (TEXT('\\') == *pch)
|
|
{
|
|
BOOL fOk;
|
|
|
|
*pch = 0;
|
|
fOk = CreateDirectory (pszPath, psa);
|
|
*pch = TEXT('\\');
|
|
|
|
// Any errors other than path alredy exists and we should
|
|
// bail out. We also get access denied when trying to
|
|
// create a root drive (i.e. c:) so check for this too.
|
|
//
|
|
if (!fOk)
|
|
{
|
|
dwErr = GetLastError ();
|
|
if (ERROR_ALREADY_EXISTS == dwErr)
|
|
{
|
|
dwErr = ERROR_SUCCESS;
|
|
}
|
|
else if ((ERROR_ACCESS_DENIED == dwErr) &&
|
|
(pch - 1 > pszPath) && (TEXT(':') == *(pch - 1)))
|
|
{
|
|
dwErr = ERROR_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ERROR_ALREADY_EXISTS == dwErr)
|
|
{
|
|
dwErr = ERROR_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
|
|
DTLNODE*
|
|
CreateKvNode(
|
|
IN LPCTSTR pszKey,
|
|
IN LPCTSTR pszValue )
|
|
|
|
/* Returns a KEYVALUE node containing a copy of 'pszKey' and 'pszValue' or
|
|
** NULL on error. It is caller's responsibility to DestroyKvNode the
|
|
** returned node.
|
|
*/
|
|
{
|
|
DTLNODE* pNode;
|
|
KEYVALUE* pkv;
|
|
|
|
pNode = DtlCreateSizedNode( sizeof(KEYVALUE), 0L );
|
|
if (!pNode)
|
|
return NULL;
|
|
|
|
pkv = (KEYVALUE* )DtlGetData( pNode );
|
|
ASSERT( pkv );
|
|
pkv->pszKey = StrDup( pszKey );
|
|
pkv->pszValue = StrDup( pszValue );
|
|
|
|
if (!pkv->pszKey || !pkv->pszValue)
|
|
{
|
|
DestroyKvNode( pNode );
|
|
return NULL;
|
|
}
|
|
|
|
return pNode;
|
|
}
|
|
|
|
|
|
DTLNODE*
|
|
CreatePszNode(
|
|
IN LPCTSTR psz )
|
|
|
|
/* Returns a node containing a copy of 'psz' or NULL on error. It is
|
|
** caller's responsibility to DestroyPszNode the returned node.
|
|
*/
|
|
{
|
|
TCHAR* pszData;
|
|
DTLNODE* pNode;
|
|
|
|
pszData = StrDup( psz );
|
|
if (!pszData)
|
|
return NULL;
|
|
|
|
pNode = DtlCreateNode( pszData, 0L );
|
|
if (!pNode)
|
|
{
|
|
Free( pszData );
|
|
return NULL;
|
|
}
|
|
|
|
return pNode;
|
|
}
|
|
|
|
|
|
VOID
|
|
DestroyPszNode(
|
|
IN DTLNODE* pdtlnode )
|
|
|
|
/* Release memory associated with string (or any simple Malloc) node
|
|
** 'pdtlnode'. See DtlDestroyList.
|
|
*/
|
|
{
|
|
TCHAR* psz;
|
|
|
|
ASSERT(pdtlnode);
|
|
psz = (TCHAR* )DtlGetData( pdtlnode );
|
|
Free0( psz );
|
|
|
|
DtlDestroyNode( pdtlnode );
|
|
}
|
|
|
|
|
|
VOID
|
|
DestroyKvNode(
|
|
IN DTLNODE* pdtlnode )
|
|
|
|
/* Release memory associated with a KEYVALUE node 'pdtlnode'. See
|
|
** DtlDestroyList.
|
|
*/
|
|
{
|
|
KEYVALUE* pkv;
|
|
|
|
ASSERT(pdtlnode);
|
|
pkv = (KEYVALUE* )DtlGetData( pdtlnode );
|
|
ASSERT(pkv);
|
|
|
|
Free0( pkv->pszKey );
|
|
Free0( pkv->pszValue );
|
|
|
|
DtlDestroyNode( pdtlnode );
|
|
}
|
|
|
|
|
|
BOOL
|
|
DeviceAndPortFromPsz(
|
|
IN TCHAR* pszDP,
|
|
OUT TCHAR** ppszDevice,
|
|
OUT TCHAR** ppszPort )
|
|
|
|
/* Loads '*ppszDevice' and '*ppszPort' with the parsed out device and port
|
|
** names from 'pszDP', a display string created with PszFromDeviceAndPort.
|
|
**
|
|
** Returns true if successful, false if 'pszDP' is not of the stated form.
|
|
** It is caller's responsibility to Free the returned '*ppszDevice' and
|
|
** '*ppszPort'.
|
|
*/
|
|
{
|
|
TCHAR szDP[ RAS_MaxDeviceName + 2 + MAX_PORT_NAME + 1 + 1 ];
|
|
INT cb;
|
|
|
|
*ppszDevice = NULL;
|
|
*ppszPort = NULL;
|
|
|
|
lstrcpyn( szDP, pszDP, sizeof(szDP) / sizeof(TCHAR) );
|
|
cb = lstrlen( szDP );
|
|
|
|
if (cb > 0)
|
|
{
|
|
TCHAR* pch;
|
|
|
|
pch = szDP + cb;
|
|
pch = CharPrev( szDP, pch );
|
|
|
|
while (pch != szDP)
|
|
{
|
|
if (*pch == TEXT(')'))
|
|
{
|
|
*pch = TEXT('\0');
|
|
}
|
|
else if (*pch == TEXT('('))
|
|
{
|
|
*ppszPort = StrDup( CharNext( pch ) );
|
|
// [pmay] backup trailing spaces
|
|
pch--;
|
|
while ((*pch == TEXT(' ')) && (pch != szDP))
|
|
pch--;
|
|
pch++;
|
|
*pch = TEXT('\0');
|
|
*ppszDevice = StrDup( szDP );
|
|
break;
|
|
}
|
|
|
|
pch = CharPrev( szDP, pch );
|
|
}
|
|
}
|
|
|
|
return (*ppszDevice && *ppszPort);
|
|
}
|
|
|
|
|
|
DTLNODE*
|
|
DuplicateKvNode(
|
|
IN DTLNODE* pdtlnode )
|
|
|
|
/* Duplicates KEYVALUE node 'pdtlnode'. See DtlDuplicateList.
|
|
**
|
|
** Returns the address of the allocated node or NULL if out of memory. It
|
|
** is caller's responsibility to free the returned node.
|
|
*/
|
|
{
|
|
DTLNODE* pNode;
|
|
KEYVALUE* pKv;
|
|
|
|
pKv = (KEYVALUE* )DtlGetData( pdtlnode );
|
|
ASSERT(pKv);
|
|
|
|
pNode = CreateKvNode( pKv->pszKey, pKv->pszValue );
|
|
if (pNode)
|
|
{
|
|
DtlPutNodeId( pNode, DtlGetNodeId( pdtlnode ) );
|
|
}
|
|
return pNode;
|
|
}
|
|
|
|
|
|
DTLNODE*
|
|
DuplicatePszNode(
|
|
IN DTLNODE* pdtlnode )
|
|
|
|
/* Duplicates string node 'pdtlnode'. See DtlDuplicateList.
|
|
**
|
|
** Returns the address of the allocated node or NULL if out of memory. It
|
|
** is caller's responsibility to free the returned node.
|
|
*/
|
|
{
|
|
DTLNODE* pNode;
|
|
TCHAR* psz;
|
|
|
|
psz = (TCHAR* )DtlGetData( pdtlnode );
|
|
ASSERT(psz);
|
|
|
|
pNode = CreatePszNode( psz );
|
|
if (pNode)
|
|
{
|
|
DtlPutNodeId( pNode, DtlGetNodeId( pdtlnode ) );
|
|
}
|
|
return pNode;
|
|
}
|
|
|
|
|
|
BOOL
|
|
FFileExists(
|
|
IN TCHAR* pszPath )
|
|
|
|
/* Returns true if the path 'pszPath' exists, false otherwise.
|
|
*/
|
|
{
|
|
WIN32_FIND_DATA finddata;
|
|
HANDLE h;
|
|
DWORD dwErr;
|
|
|
|
if ((h = FindFirstFile( pszPath, &finddata )) != INVALID_HANDLE_VALUE)
|
|
{
|
|
|
|
FindClose( h );
|
|
return TRUE;
|
|
}
|
|
|
|
dwErr = GetLastError();
|
|
|
|
TRACE1("FindFirstFile failed with 0x%x",
|
|
dwErr);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
FIsTcpipInstalled()
|
|
{
|
|
BOOL fInstalled;
|
|
SC_HANDLE ScmHandle;
|
|
ScmHandle = OpenSCManager(NULL, NULL, GENERIC_READ);
|
|
if (!ScmHandle) {
|
|
fInstalled = FALSE;
|
|
} else {
|
|
static const TCHAR c_szTcpip[] = TEXT("Tcpip");
|
|
SC_HANDLE ServiceHandle;
|
|
ServiceHandle = OpenService(ScmHandle, c_szTcpip, SERVICE_QUERY_STATUS);
|
|
if (!ServiceHandle) {
|
|
fInstalled = FALSE;
|
|
} else {
|
|
SERVICE_STATUS ServiceStatus;
|
|
if (!QueryServiceStatus(ServiceHandle, &ServiceStatus)) {
|
|
fInstalled = FALSE;
|
|
} else {
|
|
fInstalled = (ServiceStatus.dwCurrentState == SERVICE_RUNNING);
|
|
}
|
|
CloseServiceHandle(ServiceHandle);
|
|
}
|
|
CloseServiceHandle(ScmHandle);
|
|
}
|
|
return fInstalled;
|
|
}
|
|
|
|
BOOL
|
|
FIsUserAdminOrPowerUser()
|
|
{
|
|
SID_IDENTIFIER_AUTHORITY SidAuth = SECURITY_NT_AUTHORITY;
|
|
PSID psid;
|
|
BOOL fIsMember = FALSE;
|
|
BOOL fRet = FALSE;
|
|
SID sidLocalSystem = { 1, 1,
|
|
SECURITY_NT_AUTHORITY,
|
|
SECURITY_LOCAL_SYSTEM_RID };
|
|
|
|
|
|
// Check to see if running under local system first
|
|
//
|
|
if (!CheckTokenMembership( NULL, &sidLocalSystem, &fIsMember ))
|
|
{
|
|
TRACE( "CheckTokenMemberShip for local system failed.");
|
|
fIsMember = FALSE;
|
|
}
|
|
|
|
fRet = fIsMember;
|
|
|
|
if (!fIsMember)
|
|
{
|
|
// Allocate a SID for the Administrators group and check to see
|
|
// if the user is a member.
|
|
//
|
|
if (AllocateAndInitializeSid( &SidAuth, 2,
|
|
SECURITY_BUILTIN_DOMAIN_RID,
|
|
DOMAIN_ALIAS_RID_ADMINS,
|
|
0, 0, 0, 0, 0, 0,
|
|
&psid ))
|
|
{
|
|
if (!CheckTokenMembership( NULL, psid, &fIsMember ))
|
|
{
|
|
TRACE( "CheckTokenMemberShip for admins failed.");
|
|
fIsMember = FALSE;
|
|
}
|
|
|
|
FreeSid( psid );
|
|
|
|
// Changes to the Windows 2000 permission model mean that regular Users
|
|
// on workstations are in the power user group. So we no longer want to
|
|
// check for power user.
|
|
#if 0
|
|
if (!fIsMember)
|
|
{
|
|
// They're not a member of the Administrators group so allocate a
|
|
// SID for the Power Users group and check to see
|
|
// if the user is a member.
|
|
//
|
|
if (AllocateAndInitializeSid( &SidAuth, 2,
|
|
SECURITY_BUILTIN_DOMAIN_RID,
|
|
DOMAIN_ALIAS_RID_POWER_USERS,
|
|
0, 0, 0, 0, 0, 0,
|
|
&psid ))
|
|
{
|
|
if (!CheckTokenMembership( NULL, psid, &fIsMember ))
|
|
{
|
|
TRACE( "CheckTokenMemberShip for power users failed.");
|
|
fIsMember = FALSE;
|
|
}
|
|
|
|
FreeSid( psid );
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
fRet = fIsMember;
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
VOID*
|
|
Free0(
|
|
VOID* p )
|
|
|
|
/* Like Free, but deals with NULL 'p'.
|
|
*/
|
|
{
|
|
if (!p)
|
|
return NULL;
|
|
|
|
return Free( p );
|
|
}
|
|
|
|
|
|
DWORD
|
|
GetInstalledProtocols(
|
|
void
|
|
)
|
|
{
|
|
|
|
ASSERT(FALSE);
|
|
|
|
return 0 ;
|
|
}
|
|
|
|
|
|
LONG
|
|
RegQueryDword (HKEY hkey, LPCTSTR szValueName, LPDWORD pdwValue)
|
|
{
|
|
// Get the value.
|
|
//
|
|
DWORD dwType;
|
|
DWORD cbData = sizeof(DWORD);
|
|
LONG lr = RegQueryValueEx (hkey, szValueName, NULL, &dwType,
|
|
(LPBYTE)pdwValue, &cbData);
|
|
|
|
// It's type should be REG_DWORD. (duh).
|
|
//
|
|
if ((ERROR_SUCCESS == lr) && (REG_DWORD != dwType))
|
|
{
|
|
lr = ERROR_INVALID_DATATYPE;
|
|
}
|
|
|
|
// Make sure we initialize the output value on error.
|
|
// (We don't know for sure that RegQueryValueEx does this.)
|
|
//
|
|
if (ERROR_SUCCESS != lr)
|
|
{
|
|
*pdwValue = 0;
|
|
}
|
|
|
|
return lr;
|
|
}
|
|
|
|
BOOL
|
|
FProtocolEnabled(
|
|
HKEY hkeyProtocol,
|
|
BOOL fRasSrv,
|
|
BOOL fRouter )
|
|
{
|
|
static const TCHAR c_szRegValEnableIn[] = TEXT("EnableIn");
|
|
static const TCHAR c_szRegValEnableRoute[] = TEXT("EnableRoute");
|
|
|
|
DWORD dwValue;
|
|
LONG lr;
|
|
|
|
if (fRasSrv)
|
|
{
|
|
lr = RegQueryDword(hkeyProtocol, c_szRegValEnableIn, &dwValue);
|
|
if ((ERROR_FILE_NOT_FOUND == lr) ||
|
|
((ERROR_SUCCESS == lr) && (dwValue != 0)))
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (fRouter)
|
|
{
|
|
lr = RegQueryDword(hkeyProtocol, c_szRegValEnableRoute, &dwValue);
|
|
if ((ERROR_FILE_NOT_FOUND == lr) ||
|
|
((ERROR_SUCCESS == lr) && (dwValue != 0)))
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
DWORD
|
|
DwGetInstalledProtocolsEx(
|
|
BOOL fRouter,
|
|
BOOL fRasCli,
|
|
BOOL fRasSrv )
|
|
|
|
/* Returns a bit field containing NP_<protocol> flags for the installed
|
|
** PPP protocols. The term "installed" here includes enabling in RAS
|
|
** Setup.
|
|
*/
|
|
{
|
|
static const TCHAR c_szRegKeyIp[] = TEXT("SYSTEM\\CurrentControlSet\\Services\\Tcpip");
|
|
static const TCHAR c_szRegKeyIpx[] = TEXT("SYSTEM\\CurrentControlSet\\Services\\NwlnkIpx");
|
|
static const TCHAR c_szRegKeyNbf[] = TEXT("SYSTEM\\CurrentControlSet\\Services\\Nbf");
|
|
|
|
static const TCHAR c_szRegKeyRemoteAccessParams[] =
|
|
TEXT("SYSTEM\\CurrentControlSet\\Services\\RemoteAccess\\Parameters");
|
|
|
|
static const TCHAR c_szRegSubkeyIp[] = TEXT("Ip");
|
|
static const TCHAR c_szRegSubkeyIpx[] = TEXT("Ipx");
|
|
static const TCHAR c_szRegSubkeyNbf[] = TEXT("Nbf");
|
|
|
|
DWORD dwfInstalledProtocols = 0;
|
|
|
|
// First check if the protocols are installed.
|
|
//
|
|
struct INSTALLED_PROTOCOL_INFO
|
|
{
|
|
DWORD dwFlag;
|
|
LPCTSTR pszRegKey;
|
|
LPCTSTR pszSubkey;
|
|
};
|
|
static const struct INSTALLED_PROTOCOL_INFO c_aProtocolInfo[] =
|
|
{
|
|
{ NP_Ip, c_szRegKeyIp, c_szRegSubkeyIp },
|
|
{ NP_Ipx, c_szRegKeyIpx, c_szRegSubkeyIpx },
|
|
{ NP_Nbf, c_szRegKeyNbf, c_szRegSubkeyNbf },
|
|
};
|
|
|
|
#define celems(_x) (sizeof(_x) / sizeof(_x[0]))
|
|
|
|
HKEY hkey;
|
|
int i;
|
|
for (i = 0; i < celems (c_aProtocolInfo); i++)
|
|
{
|
|
const struct INSTALLED_PROTOCOL_INFO* pInfo = c_aProtocolInfo + i;
|
|
|
|
if (RegOpenKey( HKEY_LOCAL_MACHINE, pInfo->pszRegKey, &hkey ) == 0)
|
|
{
|
|
dwfInstalledProtocols |= pInfo->dwFlag;
|
|
RegCloseKey( hkey );
|
|
}
|
|
}
|
|
|
|
// Now see if they are to be used for the router and/or server.
|
|
// The client uses the protocols if they are installed and not excluded
|
|
// in the phonebook entry.
|
|
//
|
|
if ((fRouter || fRasSrv) && dwfInstalledProtocols)
|
|
{
|
|
if (RegOpenKey( HKEY_LOCAL_MACHINE, c_szRegKeyRemoteAccessParams, &hkey ) == 0)
|
|
{
|
|
for (i = 0; i < celems (c_aProtocolInfo); i++)
|
|
{
|
|
const struct INSTALLED_PROTOCOL_INFO* pInfo = c_aProtocolInfo + i;
|
|
|
|
// If the protocol is installed (as determined above), check
|
|
// to see if its enabled.
|
|
//
|
|
if (dwfInstalledProtocols & pInfo->dwFlag)
|
|
{
|
|
HKEY hkeyProtocol;
|
|
if (RegOpenKey( hkey, pInfo->pszSubkey, &hkeyProtocol ) == 0)
|
|
{
|
|
if (!FProtocolEnabled( hkeyProtocol, fRasSrv, fRouter ))
|
|
{
|
|
dwfInstalledProtocols &= ~pInfo->dwFlag;
|
|
}
|
|
|
|
RegCloseKey( hkeyProtocol );
|
|
}
|
|
}
|
|
}
|
|
|
|
RegCloseKey( hkey );
|
|
}
|
|
else
|
|
{
|
|
dwfInstalledProtocols = 0;
|
|
}
|
|
}
|
|
|
|
|
|
TRACE1("GetInstalledProtocolsEx=$%x. ",dwfInstalledProtocols);
|
|
|
|
return dwfInstalledProtocols;
|
|
}
|
|
|
|
DWORD
|
|
GetInstalledProtocolsEx(HANDLE hConnection,
|
|
BOOL fRouter,
|
|
BOOL fRasCli,
|
|
BOOL fRasSrv)
|
|
{
|
|
RAS_RPC *pRasRpcConnection = (RAS_RPC *) hConnection;
|
|
|
|
if( NULL == pRasRpcConnection
|
|
|| pRasRpcConnection->fLocal)
|
|
{
|
|
return DwGetInstalledProtocolsEx(fRouter,
|
|
fRasCli,
|
|
fRasSrv);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Remote Server
|
|
//
|
|
return RemoteGetInstalledProtocolsEx(hConnection,
|
|
fRouter,
|
|
fRasCli,
|
|
fRasSrv);
|
|
}
|
|
}
|
|
|
|
/*
|
|
|
|
DWORD
|
|
GetInstalledProtocolsEx(
|
|
BOOL fRouter,
|
|
BOOL fRasCli,
|
|
BOOL fRasSrv )
|
|
|
|
{
|
|
|
|
DWORD dwRetCode;
|
|
DWORD dwfInstalledProtocols;
|
|
|
|
|
|
dwRetCode = dwGetInstalledProtocols ( &dwfInstalledProtocols,
|
|
fRouter,
|
|
fRasCli,
|
|
fRasSrv);
|
|
|
|
TRACE2("GetInstalledProtocols=$%x. dwErr = %d",dwfInstalledProtocols, dwRetCode);
|
|
|
|
return dwfInstalledProtocols;
|
|
} */
|
|
|
|
|
|
CHAR
|
|
HexChar(
|
|
IN BYTE byte )
|
|
|
|
/* Returns an ASCII hexidecimal character corresponding to 0 to 15 value,
|
|
** 'byte'.
|
|
*/
|
|
{
|
|
const CHAR* pszHexDigits = "0123456789ABCDEF";
|
|
|
|
if (byte >= 0 && byte < 16)
|
|
return pszHexDigits[ byte ];
|
|
else
|
|
return '0';
|
|
}
|
|
|
|
|
|
BYTE
|
|
HexValue(
|
|
IN CHAR ch )
|
|
|
|
/* Returns the value 0 to 15 of hexadecimal character 'ch'.
|
|
*/
|
|
{
|
|
if (ch >= '0' && ch <= '9')
|
|
return (BYTE )(ch - '0');
|
|
else if (ch >= 'A' && ch <= 'F')
|
|
return (BYTE )((ch - 'A') + 10);
|
|
else if (ch >= 'a' && ch <= 'f')
|
|
return (BYTE )((ch - 'a') + 10);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsAllWhite(
|
|
IN LPCTSTR psz )
|
|
|
|
/* Returns true if 'psz' consists entirely of spaces and tabs. NULL
|
|
** pointers and empty strings are considered all white. Otherwise,
|
|
** returns false.
|
|
*/
|
|
{
|
|
LPCTSTR pszThis;
|
|
|
|
for (pszThis = psz; *pszThis != TEXT('\0'); ++pszThis)
|
|
{
|
|
if (*pszThis != TEXT(' ') && *pszThis != TEXT('\t'))
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void
|
|
IpHostAddrToPsz(
|
|
IN DWORD dwAddr,
|
|
OUT LPTSTR pszBuffer )
|
|
|
|
// Converts an IP address in host byte order to its
|
|
// string representation.
|
|
// pszBuffer should be allocated by the caller and be
|
|
// at least 16 characters long.
|
|
//
|
|
{
|
|
BYTE* pb = (BYTE*)&dwAddr;
|
|
static const TCHAR c_szIpAddr [] = TEXT("%d.%d.%d.%d");
|
|
wsprintf (pszBuffer, c_szIpAddr, pb[3], pb[2], pb[1], pb[0]);
|
|
}
|
|
|
|
DWORD
|
|
IpPszToHostAddr(
|
|
IN LPCTSTR cp )
|
|
|
|
// Converts an IP address represented as a string to
|
|
// host byte order.
|
|
//
|
|
{
|
|
DWORD val, base, n;
|
|
TCHAR c;
|
|
DWORD parts[4], *pp = parts;
|
|
|
|
again:
|
|
// Collect number up to ``.''.
|
|
// Values are specified as for C:
|
|
// 0x=hex, 0=octal, other=decimal.
|
|
//
|
|
val = 0; base = 10;
|
|
if (*cp == TEXT('0'))
|
|
base = 8, cp++;
|
|
if (*cp == TEXT('x') || *cp == TEXT('X'))
|
|
base = 16, cp++;
|
|
while (c = *cp)
|
|
{
|
|
if ((c >= TEXT('0')) && (c <= TEXT('9')))
|
|
{
|
|
val = (val * base) + (c - TEXT('0'));
|
|
cp++;
|
|
continue;
|
|
}
|
|
if ((base == 16) &&
|
|
( ((c >= TEXT('0')) && (c <= TEXT('9'))) ||
|
|
((c >= TEXT('A')) && (c <= TEXT('F'))) ||
|
|
((c >= TEXT('a')) && (c <= TEXT('f'))) ))
|
|
{
|
|
val = (val << 4) + (c + 10 - (
|
|
((c >= TEXT('a')) && (c <= TEXT('f')))
|
|
? TEXT('a')
|
|
: TEXT('A') ) );
|
|
cp++;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
if (*cp == TEXT('.'))
|
|
{
|
|
// Internet format:
|
|
// a.b.c.d
|
|
// a.b.c (with c treated as 16-bits)
|
|
// a.b (with b treated as 24 bits)
|
|
//
|
|
if (pp >= parts + 3)
|
|
return (DWORD) -1;
|
|
*pp++ = val, cp++;
|
|
goto again;
|
|
}
|
|
|
|
// Check for trailing characters.
|
|
//
|
|
if (*cp && (*cp != TEXT(' ')))
|
|
return 0xffffffff;
|
|
|
|
*pp++ = val;
|
|
|
|
// Concoct the address according to
|
|
// the number of parts specified.
|
|
//
|
|
n = (DWORD) (pp - parts);
|
|
switch (n)
|
|
{
|
|
case 1: // a -- 32 bits
|
|
val = parts[0];
|
|
break;
|
|
|
|
case 2: // a.b -- 8.24 bits
|
|
val = (parts[0] << 24) | (parts[1] & 0xffffff);
|
|
break;
|
|
|
|
case 3: // a.b.c -- 8.8.16 bits
|
|
val = (parts[0] << 24) | ((parts[1] & 0xff) << 16) |
|
|
(parts[2] & 0xffff);
|
|
break;
|
|
|
|
case 4: // a.b.c.d -- 8.8.8.8 bits
|
|
val = (parts[0] << 24) | ((parts[1] & 0xff) << 16) |
|
|
((parts[2] & 0xff) << 8) | (parts[3] & 0xff);
|
|
break;
|
|
|
|
default:
|
|
return 0xffffffff;
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
|
|
#if 0
|
|
BOOL
|
|
IsNullTerminatedA(
|
|
IN CHAR* psz,
|
|
IN DWORD dwSize )
|
|
|
|
/* Returns true is 'psz' contains a null character somewhere in it's
|
|
** 'dwSize' bytes, false otherwise.
|
|
*/
|
|
{
|
|
CHAR* pszThis;
|
|
CHAR* pszEnd;
|
|
|
|
pszEnd = psz + dwSize;
|
|
for (pszThis = psz; pszThis < pszEnd; ++pszThis)
|
|
{
|
|
if (*pszThis == '\0')
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
|
|
TCHAR*
|
|
LToT(
|
|
LONG lValue,
|
|
TCHAR* pszBuf,
|
|
INT nRadix )
|
|
|
|
/* Like ltoa, but returns TCHAR*.
|
|
*/
|
|
{
|
|
#ifdef UNICODE
|
|
WCHAR szBuf[ MAXLTOTLEN + 1 ];
|
|
|
|
ASSERT(nRadix==10||nRadix==16);
|
|
|
|
if (nRadix == 10)
|
|
wsprintf( pszBuf, TEXT("%d"), lValue );
|
|
else
|
|
wsprintf( pszBuf, TEXT("%x"), lValue );
|
|
#else
|
|
_ltoa( lValue, pszBuf, nRadix );
|
|
#endif
|
|
|
|
return pszBuf;
|
|
}
|
|
|
|
|
|
|
|
LONG
|
|
TToL(
|
|
TCHAR *pszBuf )
|
|
|
|
/* Like atol, but accepts TCHAR*.
|
|
*/
|
|
{
|
|
CHAR* psz;
|
|
CHAR szBuf[ MAXLTOTLEN + 1 ];
|
|
|
|
#ifdef UNICODE
|
|
psz = szBuf;
|
|
|
|
WideCharToMultiByte(
|
|
CP_ACP, 0, pszBuf, -1, psz, MAXLTOTLEN + 1, NULL, NULL );
|
|
#else
|
|
psz = pszBuf;
|
|
#endif
|
|
|
|
return atol( psz );
|
|
}
|
|
|
|
|
|
TCHAR*
|
|
PszFromDeviceAndPort(
|
|
IN TCHAR* pszDevice,
|
|
IN TCHAR* pszPort )
|
|
|
|
/* Returns address of heap block psz containing the MXS modem list display
|
|
** form, i.e. the device name 'pszDevice' followed by the port name
|
|
** 'pszPort'. It's caller's responsibility to Free the returned string.
|
|
*/
|
|
{
|
|
/* If you're thinking of changing this format string be aware that
|
|
** DeviceAndPortFromPsz parses it.
|
|
*/
|
|
const TCHAR* pszF = TEXT("%s (%s)");
|
|
|
|
TCHAR* pszResult;
|
|
TCHAR* pszD;
|
|
TCHAR* pszP;
|
|
|
|
if (pszDevice)
|
|
pszD = pszDevice;
|
|
else
|
|
pszD = TEXT("");
|
|
|
|
if (pszPort)
|
|
pszP = pszPort;
|
|
else
|
|
pszP = TEXT("");
|
|
|
|
pszResult = Malloc(
|
|
(lstrlen( pszD ) + lstrlen( pszP ) + lstrlen( pszF ) + 1)
|
|
* sizeof(TCHAR) );
|
|
|
|
if (pszResult)
|
|
wsprintf( pszResult, pszF, pszD, pszP );
|
|
|
|
return pszResult;
|
|
}
|
|
|
|
|
|
TCHAR*
|
|
PszFromId(
|
|
IN HINSTANCE hInstance,
|
|
IN DWORD dwStringId )
|
|
|
|
/* String resource message loader routine.
|
|
**
|
|
** Returns the address of a heap block containing the string corresponding
|
|
** to string resource 'dwStringId' or NULL if error. It is caller's
|
|
** responsibility to Free the returned string.
|
|
*/
|
|
{
|
|
HRSRC hrsrc;
|
|
TCHAR* pszBuf = NULL;
|
|
int cchBuf = 256;
|
|
int cchGot;
|
|
|
|
for (;;)
|
|
{
|
|
pszBuf = Malloc( cchBuf * sizeof(TCHAR) );
|
|
if (!pszBuf)
|
|
break;
|
|
|
|
/* LoadString wants to deal with character-counts rather than
|
|
** byte-counts...weird. Oh, and if you're thinking I could
|
|
** FindResource then SizeofResource to figure out the string size, be
|
|
** advised it doesn't work. From perusing the LoadString source, it
|
|
** appears the RT_STRING resource type requests a segment of 16
|
|
** strings not an individual string.
|
|
*/
|
|
cchGot = LoadString( hInstance, (UINT )dwStringId, pszBuf, cchBuf );
|
|
|
|
if (cchGot < cchBuf - 1)
|
|
{
|
|
|
|
/* Good, got the whole string. Reduce heap block to actual size
|
|
** needed.
|
|
*/
|
|
// For whistler 517008
|
|
//
|
|
TCHAR *pszTemp = NULL;
|
|
|
|
pszTemp = Realloc( pszBuf, (cchGot + 1) * sizeof(TCHAR));
|
|
|
|
if ( NULL == pszTemp )
|
|
{
|
|
Free(pszBuf);
|
|
pszBuf = NULL;
|
|
}
|
|
else
|
|
{
|
|
pszBuf = pszTemp;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
/* Uh oh, LoadStringW filled the buffer entirely which could mean the
|
|
** string was truncated. Try again with a larger buffer to be sure it
|
|
** wasn't.
|
|
*/
|
|
Free( pszBuf );
|
|
pszBuf = NULL;
|
|
cchBuf += 256;
|
|
TRACE1("Grow string buf to %d",cchBuf);
|
|
}
|
|
|
|
return pszBuf;
|
|
}
|
|
|
|
|
|
#if 0
|
|
TCHAR*
|
|
PszFromError(
|
|
IN DWORD dwError )
|
|
|
|
/* Error message loader routine.
|
|
**
|
|
** Returns the address of a heap block containing the error string
|
|
** corresponding to RAS or system error code 'dwMsgid' or NULL if error.
|
|
** It is caller's responsibility to Free the returned string.
|
|
*/
|
|
{
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
|
|
BOOL
|
|
RestartComputer()
|
|
|
|
/* Called if user chooses to shut down the computer.
|
|
**
|
|
** Return false if failure, true otherwise
|
|
*/
|
|
{
|
|
HANDLE hToken = NULL; /* handle to process token */
|
|
TOKEN_PRIVILEGES tkp; /* ptr. to token structure */
|
|
BOOL fResult; /* system shutdown flag */
|
|
|
|
TRACE("RestartComputer");
|
|
|
|
/* Enable the shutdown privilege */
|
|
|
|
if (!OpenProcessToken( GetCurrentProcess(),
|
|
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
|
|
&hToken))
|
|
return FALSE;
|
|
|
|
/* Get the LUID for shutdown privilege. */
|
|
|
|
LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid);
|
|
|
|
tkp.PrivilegeCount = 1; /* one privilege to set */
|
|
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
|
|
|
/* Get shutdown privilege for this process. */
|
|
|
|
AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES) NULL, 0);
|
|
|
|
/* Cannot test the return value of AdjustTokenPrivileges. */
|
|
|
|
if (GetLastError() != ERROR_SUCCESS)
|
|
{
|
|
CloseHandle(hToken);
|
|
return FALSE;
|
|
}
|
|
|
|
if( !ExitWindowsEx(EWX_REBOOT, 0))
|
|
{
|
|
CloseHandle(hToken);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Disable shutdown privilege. */
|
|
|
|
tkp.Privileges[0].Attributes = 0;
|
|
AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES) NULL, 0);
|
|
|
|
if (GetLastError() != ERROR_SUCCESS)
|
|
{
|
|
CloseHandle(hToken);
|
|
return FALSE;
|
|
}
|
|
|
|
CloseHandle(hToken);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: PszLoadStringPcch
|
|
//
|
|
// Purpose: Load a resource string. (This function will never return NULL.)
|
|
//
|
|
// Arguments:
|
|
// hinst [in] Instance handle of module with the string resource.
|
|
// unId [in] Resource ID of the string to load.
|
|
// pcch [out] Pointer to returned character length.
|
|
//
|
|
// Returns: Pointer to the constant string.
|
|
//
|
|
// Author: shaunco 24 Mar 1997
|
|
//
|
|
// Notes: The loaded string is pointer directly into the read-only
|
|
// resource section. Any attempt to write through this pointer
|
|
// will generate an access violation.
|
|
//
|
|
// The implementations is referenced from "Win32 Binary Resource
|
|
// Formats" (MSDN) 4.8 String Table Resources
|
|
//
|
|
// User must have RCOPTIONS = -N turned on in the sources file.
|
|
//
|
|
LPCTSTR
|
|
PszLoadStringPcch (
|
|
HINSTANCE hinst,
|
|
UINT unId,
|
|
int* pcch)
|
|
{
|
|
static const WCHAR c_szwSpace[] = L" ";
|
|
LPCWSTR pszw;
|
|
int cch;
|
|
HRSRC hrsrcInfo;
|
|
|
|
ASSERT(hinst);
|
|
ASSERT(unId);
|
|
ASSERT(pcch);
|
|
|
|
pszw = c_szwSpace;
|
|
cch = 0;
|
|
|
|
// String Tables are broken up into 16 string segments. Find the segment
|
|
// containing the string we are interested in.
|
|
hrsrcInfo = FindResource(hinst, (LPTSTR)UlongToPtr((LONG)(((USHORT)unId >> 4) + 1)),
|
|
RT_STRING);
|
|
if (hrsrcInfo)
|
|
{
|
|
// Page the resource segment into memory.
|
|
HGLOBAL hglbSeg = LoadResource(hinst, hrsrcInfo);
|
|
if (hglbSeg)
|
|
{
|
|
// Lock the resource.
|
|
pszw = (LPCWSTR)LockResource(hglbSeg);
|
|
if (pszw)
|
|
{
|
|
// Move past the other strings in this segment.
|
|
// (16 strings in a segment -> & 0x0F)
|
|
unId &= 0x0F;
|
|
|
|
ASSERT(!cch); // first time through, cch should be zero
|
|
do
|
|
{
|
|
pszw += cch; // Step to start of next string
|
|
cch = *((WCHAR*)pszw++); // PASCAL like string count
|
|
}
|
|
while (unId--);
|
|
|
|
if (!cch)
|
|
{
|
|
ASSERT(0); // String resource not found
|
|
pszw = c_szwSpace;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pszw = c_szwSpace;
|
|
TRACE("PszLoadStringPcch: LockResource failed.");
|
|
}
|
|
}
|
|
else
|
|
TRACE("PszLoadStringPcch: LoadResource failed.");
|
|
}
|
|
else
|
|
TRACE("PszLoadStringPcch: FindResource failed.");
|
|
|
|
*pcch = cch;
|
|
ASSERT(*pcch);
|
|
ASSERT(pszw);
|
|
return pszw;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: PszLoadString
|
|
//
|
|
// Purpose: Load a resource string. (This function will never return NULL.)
|
|
//
|
|
// Arguments:
|
|
// hinst [in] Instance handle of module with the string resource.
|
|
// unId [in] Resource ID of the string to load.
|
|
//
|
|
// Returns: Pointer to the constant string.
|
|
//
|
|
// Author: shaunco 24 Mar 1997
|
|
//
|
|
// Notes: See PszLoadStringPcch()
|
|
//
|
|
LPCTSTR
|
|
PszLoadString (
|
|
HINSTANCE hinst,
|
|
UINT unId)
|
|
{
|
|
int cch;
|
|
return PszLoadStringPcch (hinst, unId, &cch);
|
|
}
|
|
|
|
|
|
DWORD
|
|
ShellSort(
|
|
IN VOID* pItemTable,
|
|
IN DWORD dwItemSize,
|
|
IN DWORD dwItemCount,
|
|
IN PFNCOMPARE pfnCompare )
|
|
|
|
/* Sort an array of items in-place using shell-sort.
|
|
** This function calls ShellSortIndirect to sort a table of pointers
|
|
** to table items. We then move the items into place by copying.
|
|
** This algorithm allows us to guarantee that the number
|
|
** of copies necessary in the worst case is N + 1.
|
|
**
|
|
** Note that if the caller merely needs to know the sorted order
|
|
** of the array, ShellSortIndirect should be called since that function
|
|
** avoids moving items altogether, and instead fills an array with pointers
|
|
** to the array items in the correct order. The array items can then
|
|
** be accessed through the array of pointers.
|
|
*/
|
|
{
|
|
|
|
VOID** ppItemTable;
|
|
|
|
INT N;
|
|
INT i;
|
|
BYTE *a, **p, *t = NULL;
|
|
|
|
if (!dwItemCount) { return NO_ERROR; }
|
|
|
|
|
|
/* allocate space for the table of pointers.
|
|
*/
|
|
ppItemTable = Malloc(dwItemCount * sizeof(VOID*));
|
|
if (!ppItemTable) { return ERROR_NOT_ENOUGH_MEMORY; }
|
|
|
|
|
|
/* call ShellSortIndirect to fill our table of pointers
|
|
** with the sorted positions for each table element.
|
|
*/
|
|
ShellSortIndirect(
|
|
pItemTable, ppItemTable, dwItemSize, dwItemCount, pfnCompare );
|
|
|
|
|
|
/* now that we know the sort order, move each table item into place.
|
|
** This involves going through the table of pointers making sure
|
|
** that the item which should be in 'i' is in fact in 'i', moving
|
|
** things around if necessary to achieve this condition.
|
|
*/
|
|
|
|
a = (BYTE*)pItemTable;
|
|
p = (BYTE**)ppItemTable;
|
|
N = (INT)dwItemCount;
|
|
|
|
for (i = 0; i < N; i++)
|
|
{
|
|
INT j, k;
|
|
BYTE* ai = (a + i * dwItemSize), *ak, *aj;
|
|
|
|
/* see if item 'i' is not in-place
|
|
*/
|
|
if (p[i] != ai)
|
|
{
|
|
|
|
|
|
/* item 'i' isn't in-place, so we'll have to move it.
|
|
** if we've delayed allocating a temporary buffer so far,
|
|
** we'll need one now.
|
|
*/
|
|
|
|
if (!t) {
|
|
t = Malloc(dwItemSize);
|
|
if (!t) { return ERROR_NOT_ENOUGH_MEMORY; }
|
|
}
|
|
|
|
/* save a copy of the item to be overwritten
|
|
*/
|
|
CopyMemory(t, ai, dwItemSize);
|
|
|
|
k = i;
|
|
ak = ai;
|
|
|
|
|
|
/* Now move whatever item is occupying the space where it should be.
|
|
** This may involve moving the item occupying the space where
|
|
** it should be, etc.
|
|
*/
|
|
|
|
do
|
|
{
|
|
|
|
/* copy the item which should be in position 'j'
|
|
** over the item which is currently in position 'j'.
|
|
*/
|
|
j = k;
|
|
aj = ak;
|
|
CopyMemory(aj, p[j], dwItemSize);
|
|
|
|
/* set 'k' to the position from which we copied
|
|
** into position 'j'; this is where we will copy
|
|
** the next out-of-place item in the array.
|
|
*/
|
|
ak = p[j];
|
|
k = (INT)(ak - a) / dwItemSize;
|
|
|
|
/* keep the array of position pointers up-to-date;
|
|
** the contents of 'aj' are now in their sorted position.
|
|
*/
|
|
p[j] = aj;
|
|
|
|
} while (ak != ai);
|
|
|
|
|
|
/* now write the item which we first overwrote.
|
|
*/
|
|
CopyMemory(aj, t, dwItemSize);
|
|
}
|
|
}
|
|
|
|
Free0(t);
|
|
Free(ppItemTable);
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
VOID
|
|
ShellSortIndirect(
|
|
IN VOID* pItemTable,
|
|
IN VOID** ppItemTable,
|
|
IN DWORD dwItemSize,
|
|
IN DWORD dwItemCount,
|
|
IN PFNCOMPARE pfnCompare )
|
|
|
|
/* Sorts an array of items indirectly using shell-sort.
|
|
** 'pItemTable' points to the table of items, 'dwItemCount' is the number
|
|
** of items in the table, and 'pfnCompare' is a function called
|
|
** to compare items.
|
|
**
|
|
** Rather than sort the items by moving them around,
|
|
** we sort them by initializing the table of pointers 'ppItemTable'
|
|
** with pointers such that 'ppItemTable[i]' contains a pointer
|
|
** into 'pItemTable' for the item which would be in position 'i'
|
|
** if 'pItemTable' were sorted.
|
|
**
|
|
** For instance, given an array pItemTable of 5 strings as follows
|
|
**
|
|
** pItemTable[0]: "xyz"
|
|
** pItemTable[1]: "abc"
|
|
** pItemTable[2]: "mno"
|
|
** pItemTable[3]: "qrs"
|
|
** pItemTable[4]: "def"
|
|
**
|
|
** on output ppItemTable contains the following pointers
|
|
**
|
|
** ppItemTable[0]: &pItemTable[1] ("abc")
|
|
** ppItemTable[1]: &pItemTable[4] ("def")
|
|
** ppItemTable[2]: &pItemTable[2] ("mno")
|
|
** ppItemTable[3]: &pItemTable[3] ("qrs")
|
|
** ppItemTable[4]: &pItemTable[0] ("xyz")
|
|
**
|
|
** and the contents of pItemTable are untouched.
|
|
** And the caller can print out the array in sorted order using
|
|
** for (i = 0; i < 4; i++) {
|
|
** printf("%s\n", (char *)*ppItemTable[i]);
|
|
** }
|
|
*/
|
|
{
|
|
|
|
/* The following algorithm is derived from Sedgewick's Shellsort,
|
|
** as given in "Algorithms in C++".
|
|
**
|
|
** The Shellsort algorithm sorts the table by viewing it as
|
|
** a number of interleaved arrays, each of whose elements are 'h'
|
|
** spaces apart for some 'h'. Each array is sorted separately,
|
|
** starting with the array whose elements are farthest apart and
|
|
** ending with the array whose elements are closest together.
|
|
** Since the 'last' such array always has elements next to each other,
|
|
** this degenerates to Insertion sort, but by the time we get down
|
|
** to the 'last' array, the table is pretty much sorted.
|
|
**
|
|
** The sequence of values chosen below for 'h' is 1, 4, 13, 40, 121, ...
|
|
** and the worst-case running time for the sequence is N^(3/2), where
|
|
** the running time is measured in number of comparisons.
|
|
*/
|
|
|
|
#define PFNSHELLCMP(a,b) (++Ncmp, pfnCompare((a),(b)))
|
|
|
|
DWORD dwErr;
|
|
INT i, j, h, N, Ncmp;
|
|
BYTE* a, *v, **p;
|
|
|
|
|
|
a = (BYTE*)pItemTable;
|
|
p = (BYTE**)ppItemTable;
|
|
N = (INT)dwItemCount;
|
|
Ncmp = 0;
|
|
|
|
TRACE1("ShellSortIndirect: N=%d", N);
|
|
|
|
/* Initialize the table of position pointers.
|
|
*/
|
|
for (i = 0; i < N; i++) { p[i] = (a + i * dwItemSize); }
|
|
|
|
|
|
/* Move 'h' to the largest increment in our series
|
|
*/
|
|
for (h = 1; h < N/9; h = 3 * h + 1) { }
|
|
|
|
|
|
/* For each increment in our series, sort the 'array' for that increment
|
|
*/
|
|
for ( ; h > 0; h /= 3)
|
|
{
|
|
|
|
/* For each element in the 'array', get the pointer to its
|
|
** sorted position.
|
|
*/
|
|
for (i = h; i < N; i++)
|
|
{
|
|
/* save the pointer to be inserted
|
|
*/
|
|
v = p[i]; j = i;
|
|
|
|
/* Move all the larger elements to the right
|
|
*/
|
|
while (j >= h && PFNSHELLCMP(p[j - h], v) > 0)
|
|
{
|
|
p[j] = p[j - h]; j -= h;
|
|
}
|
|
|
|
/* put the saved pointer in the position where we stopped.
|
|
*/
|
|
p[j] = v;
|
|
}
|
|
}
|
|
|
|
TRACE1("ShellSortIndirect: Ncmp=%d", Ncmp);
|
|
|
|
#undef PFNSHELLCMP
|
|
|
|
}
|
|
|
|
|
|
TCHAR*
|
|
StrDup(
|
|
LPCTSTR psz )
|
|
|
|
/* Returns heap block containing a copy of 0-terminated string 'psz' or
|
|
** NULL on error or is 'psz' is NULL. It is caller's responsibility to
|
|
** 'Free' the returned string.
|
|
*/
|
|
{
|
|
TCHAR* pszNew = NULL;
|
|
|
|
if (psz)
|
|
{
|
|
pszNew = Malloc( (lstrlen( psz ) + 1) * sizeof(TCHAR) );
|
|
if (!pszNew)
|
|
{
|
|
TRACE("StrDup Malloc failed");
|
|
return NULL;
|
|
}
|
|
|
|
lstrcpy( pszNew, psz );
|
|
}
|
|
|
|
return pszNew;
|
|
}
|
|
|
|
|
|
CHAR*
|
|
StrDupAFromTInternal(
|
|
LPCTSTR psz,
|
|
IN DWORD dwCp)
|
|
|
|
/* Returns heap block containing a copy of 0-terminated string 'psz' or
|
|
** NULL on error or is 'psz' is NULL. The output string is converted to
|
|
** MB ANSI. It is caller's responsibility to 'Free' the returned string.
|
|
*/
|
|
{
|
|
#ifdef UNICODE
|
|
|
|
CHAR* pszNew = NULL;
|
|
|
|
if (psz)
|
|
{
|
|
DWORD cb;
|
|
|
|
cb = WideCharToMultiByte( dwCp, 0, psz, -1, NULL, 0, NULL, NULL );
|
|
ASSERT(cb);
|
|
|
|
pszNew = (CHAR* )Malloc( cb + 1 );
|
|
if (!pszNew)
|
|
{
|
|
TRACE("StrDupAFromT Malloc failed");
|
|
return NULL;
|
|
}
|
|
|
|
cb = WideCharToMultiByte( dwCp, 0, psz, -1, pszNew, cb, NULL, NULL );
|
|
if (cb == 0)
|
|
{
|
|
Free( pszNew );
|
|
TRACE("StrDupAFromT conversion failed");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return pszNew;
|
|
|
|
#else // !UNICODE
|
|
|
|
return StrDup( psz );
|
|
|
|
#endif
|
|
}
|
|
|
|
CHAR*
|
|
StrDupAFromT(
|
|
LPCTSTR psz)
|
|
{
|
|
return StrDupAFromTInternal(psz, CP_UTF8);
|
|
}
|
|
|
|
CHAR*
|
|
StrDupAFromTAnsi(
|
|
LPCTSTR psz)
|
|
{
|
|
return StrDupAFromTInternal(psz, CP_ACP);
|
|
}
|
|
|
|
TCHAR*
|
|
StrDupTFromA(
|
|
LPCSTR psz )
|
|
|
|
/* Returns heap block containing a copy of 0-terminated string 'psz' or
|
|
** NULL on error or is 'psz' is NULL. The output string is converted to
|
|
** UNICODE. It is caller's responsibility to Free the returned string.
|
|
*/
|
|
{
|
|
#ifdef UNICODE
|
|
|
|
return StrDupWFromA( psz );
|
|
|
|
#else // !UNICODE
|
|
|
|
return StrDup( psz );
|
|
|
|
#endif
|
|
}
|
|
|
|
TCHAR*
|
|
StrDupTFromAUsingAnsiEncoding(
|
|
LPCSTR psz )
|
|
{
|
|
#ifdef UNICODE
|
|
|
|
return StrDupWFromAInternal(psz, CP_ACP);
|
|
|
|
#else // !UNICODE
|
|
|
|
return StrDup( psz );
|
|
|
|
#endif
|
|
}
|
|
|
|
TCHAR*
|
|
StrDupTFromW(
|
|
LPCWSTR psz )
|
|
|
|
/* Returns heap block containing a copy of 0-terminated string 'psz' or
|
|
** NULL on error or is 'psz' is NULL. The output string is converted to
|
|
** UNICODE. It is caller's responsibility to Free the returned string.
|
|
*/
|
|
{
|
|
#ifdef UNICODE
|
|
|
|
return StrDup( psz );
|
|
|
|
#else // !UNICODE
|
|
|
|
CHAR* pszNew = NULL;
|
|
|
|
if (psz)
|
|
{
|
|
DWORD cb;
|
|
|
|
cb = WideCharToMultiByte( CP_UTF8, 0, psz, -1, NULL, 0, NULL, NULL );
|
|
ASSERT(cb);
|
|
|
|
pszNew = (CHAR* )Malloc( cb + 1 );
|
|
if (!pszNew)
|
|
{
|
|
TRACE("StrDupTFromW Malloc failed");
|
|
return NULL;
|
|
}
|
|
|
|
cb = WideCharToMultiByte( CP_UTF8, 0, psz, -1, pszNew, cb, NULL, NULL );
|
|
if (cb == 0)
|
|
{
|
|
Free( pszNew );
|
|
TRACE("StrDupTFromW conversion failed");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return pszNew;
|
|
|
|
#endif
|
|
}
|
|
|
|
|
|
WCHAR*
|
|
StrDupWFromAInternal(
|
|
LPCSTR psz,
|
|
UINT uiCodePage)
|
|
|
|
/* Returns heap block containing a copy of 0-terminated string 'psz' or
|
|
** NULL on error or if 'psz' is NULL. The output string is converted to
|
|
** UNICODE. It is caller's responsibility to Free the returned string.
|
|
*/
|
|
{
|
|
WCHAR* pszNew = NULL;
|
|
|
|
if (psz)
|
|
{
|
|
DWORD cb;
|
|
|
|
cb = MultiByteToWideChar( uiCodePage, 0, psz, -1, NULL, 0 );
|
|
ASSERT(cb);
|
|
|
|
pszNew = Malloc( (cb + 1) * sizeof(TCHAR) );
|
|
if (!pszNew)
|
|
{
|
|
TRACE("StrDupWFromA Malloc failed");
|
|
return NULL;
|
|
}
|
|
|
|
cb = MultiByteToWideChar( uiCodePage, 0, psz, -1, pszNew, cb );
|
|
if (cb == 0)
|
|
{
|
|
Free( pszNew );
|
|
TRACE("StrDupWFromA conversion failed");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return pszNew;
|
|
}
|
|
|
|
WCHAR*
|
|
StrDupWFromA(
|
|
LPCSTR psz )
|
|
{
|
|
return StrDupWFromAInternal(psz, CP_UTF8);
|
|
}
|
|
|
|
WCHAR*
|
|
StrDupWFromT(
|
|
LPCTSTR psz )
|
|
|
|
/* Returns heap block containing a copy of 0-terminated string 'psz' or
|
|
** NULL on error or if 'psz' is NULL. The output string is converted to
|
|
** UNICODE. It is caller's responsibility to Free the returned string.
|
|
*/
|
|
{
|
|
#ifdef UNICODE
|
|
|
|
return StrDup( psz );
|
|
|
|
#else // !UNICODE
|
|
|
|
WCHAR* pszNew = NULL;
|
|
|
|
if (psz)
|
|
{
|
|
DWORD cb;
|
|
|
|
cb = MultiByteToWideChar( CP_UTF8, 0, psz, -1, NULL, 0 );
|
|
ASSERT(cb);
|
|
|
|
pszNew = Malloc( (cb + 1) * sizeof(TCHAR) );
|
|
if (!pszNew)
|
|
{
|
|
TRACE("StrDupWFromT Malloc failed");
|
|
return NULL;
|
|
}
|
|
|
|
cb = MultiByteToWideChar( CP_UTF8, 0, psz, -1, pszNew, cb );
|
|
if (cb == 0)
|
|
{
|
|
Free( pszNew );
|
|
TRACE1("StrDupWFromT conversion failed");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return pszNew;
|
|
#endif
|
|
}
|
|
|
|
WCHAR*
|
|
StrDupWFromAUsingAnsiEncoding(
|
|
LPCSTR psz )
|
|
{
|
|
return StrDupWFromAInternal(psz, CP_ACP);
|
|
}
|
|
|
|
DWORD
|
|
StrCpyWFromA(
|
|
WCHAR* pszDst,
|
|
LPCSTR pszSrc,
|
|
DWORD dwDstChars)
|
|
{
|
|
DWORD cb, dwErr;
|
|
|
|
cb = MultiByteToWideChar( CP_UTF8, 0, pszSrc, -1, pszDst, dwDstChars );
|
|
if (cb == 0)
|
|
{
|
|
dwErr = GetLastError();
|
|
TRACE1("StrCpyWFromA conversion failed %x", dwErr);
|
|
dwErr;
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
DWORD
|
|
StrCpyAFromW(
|
|
LPSTR pszDst,
|
|
LPCWSTR pszSrc,
|
|
DWORD dwDstChars)
|
|
{
|
|
DWORD cb, dwErr;
|
|
|
|
cb = WideCharToMultiByte(
|
|
CP_UTF8, 0, pszSrc, -1,
|
|
pszDst, dwDstChars, NULL, NULL );
|
|
|
|
if (cb == 0)
|
|
{
|
|
dwErr = GetLastError();
|
|
TRACE1("StrCpyAFromW conversion failed %x", dwErr);
|
|
dwErr;
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
DWORD
|
|
StrCpyAFromWUsingAnsiEncoding(
|
|
LPSTR pszDst,
|
|
LPCWSTR pszSrc,
|
|
DWORD dwDstChars)
|
|
{
|
|
DWORD cb, dwErr;
|
|
|
|
cb = WideCharToMultiByte(
|
|
CP_ACP, 0, pszSrc, -1,
|
|
pszDst, dwDstChars, NULL, NULL );
|
|
|
|
if (cb == 0)
|
|
{
|
|
dwErr = GetLastError();
|
|
TRACE1("StrCpyAFromWUsingAnsiEncoding conversion failed %x", dwErr);
|
|
dwErr;
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
DWORD
|
|
StrCpyWFromAUsingAnsiEncoding(
|
|
WCHAR* pszDst,
|
|
LPCSTR pszSrc,
|
|
DWORD dwDstChars)
|
|
{
|
|
DWORD cb, dwErr;
|
|
|
|
*pszDst = L'\0';
|
|
cb = MultiByteToWideChar( CP_ACP, 0, pszSrc, -1, pszDst, dwDstChars );
|
|
if (cb == 0)
|
|
{
|
|
dwErr = GetLastError();
|
|
TRACE1("StrCpyWFromA conversion failed %x", dwErr);
|
|
dwErr;
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
TCHAR*
|
|
StripPath(
|
|
IN TCHAR* pszPath )
|
|
|
|
/* Returns a pointer to the file name within 'pszPath'.
|
|
*/
|
|
{
|
|
TCHAR* p;
|
|
|
|
p = pszPath + lstrlen( pszPath );
|
|
|
|
while (p > pszPath)
|
|
{
|
|
if (*p == TEXT('\\') || *p == TEXT('/') || *p == TEXT(':'))
|
|
{
|
|
p = CharNext( p );
|
|
break;
|
|
}
|
|
|
|
p = CharPrev( pszPath, p );
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
|
|
int
|
|
StrNCmpA(
|
|
IN CHAR* psz1,
|
|
IN CHAR* psz2,
|
|
IN INT nLen )
|
|
|
|
/* Like strncmp, which is not in Win32 for some reason.
|
|
*/
|
|
{
|
|
INT i;
|
|
|
|
for (i= 0; i < nLen; ++i)
|
|
{
|
|
if (*psz1 == *psz2)
|
|
{
|
|
if (*psz1 == '\0')
|
|
return 0;
|
|
}
|
|
else if (*psz1 < *psz2)
|
|
return -1;
|
|
else
|
|
return 1;
|
|
|
|
++psz1;
|
|
++psz2;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
CHAR*
|
|
StrStrA(
|
|
IN CHAR* psz1,
|
|
IN CHAR* psz2 )
|
|
|
|
/* Like strstr, which is not in Win32.
|
|
*/
|
|
{
|
|
CHAR* psz;
|
|
INT nLen2;
|
|
|
|
if (!psz1 || !psz2 || !*psz1 || !*psz2)
|
|
return NULL;
|
|
|
|
nLen2 = lstrlenA( psz2 );
|
|
|
|
for (psz = psz1;
|
|
*psz && StrNCmpA( psz, psz2, nLen2 ) != 0;
|
|
++psz);
|
|
|
|
if (*psz)
|
|
return psz;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
|
|
TCHAR*
|
|
UnNull(
|
|
TCHAR* psz )
|
|
|
|
// Returns 'psz' or, if NULL, empty string.
|
|
//
|
|
{
|
|
return (psz) ? psz : TEXT("");
|
|
}
|
|
|
|
DWORD
|
|
DwGetExpandedDllPath(LPTSTR pszDllPath,
|
|
LPTSTR *ppszExpandedDllPath)
|
|
{
|
|
DWORD dwErr = 0;
|
|
DWORD dwSize = 0;
|
|
|
|
//
|
|
// find the size of the expanded string
|
|
//
|
|
if (0 == (dwSize =
|
|
ExpandEnvironmentStrings(pszDllPath,
|
|
NULL,
|
|
0)))
|
|
{
|
|
dwErr = GetLastError();
|
|
goto done;
|
|
}
|
|
|
|
*ppszExpandedDllPath = LocalAlloc(
|
|
LPTR,
|
|
dwSize * sizeof (TCHAR));
|
|
|
|
if (NULL == *ppszExpandedDllPath)
|
|
{
|
|
dwErr = GetLastError();
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// Get the expanded string
|
|
//
|
|
if (0 == ExpandEnvironmentStrings(
|
|
pszDllPath,
|
|
*ppszExpandedDllPath,
|
|
dwSize))
|
|
{
|
|
dwErr = GetLastError();
|
|
}
|
|
|
|
done:
|
|
return dwErr;
|
|
|
|
}
|
|
|
|
|
|
|