763 lines
19 KiB
C++
763 lines
19 KiB
C++
//+----------------------------------------------------------------------------
|
|
//
|
|
// Scheduling Agent Service
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1992 - 1996.
|
|
//
|
|
// File: sageset.cxx
|
|
//
|
|
// Contents: Support for Sage Settings
|
|
//
|
|
// History: 18-Jul-96 EricB created
|
|
// 3-06-1997 DavidMun added code to create sagerun argument
|
|
// 4-16-1997 DavidMun made it use strings from UI instead of
|
|
// from job object.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "..\pch\headers.hxx"
|
|
#pragma hdrstop
|
|
#include <mstask.h>
|
|
#include <job_cls.hxx>
|
|
#include <debug.hxx>
|
|
#include <sage.hxx>
|
|
#include "sageset.hxx"
|
|
#include "..\inc\misc.hxx"
|
|
#include "..\folderui\macros.h" // get ARRAYLEN macro
|
|
|
|
const CHAR SAGE_KEY[] = "SOFTWARE\\Microsoft\\Plus!\\System Agent\\SAGE";
|
|
const CHAR PROGRAM_VALUE[] ="Program";
|
|
const CHAR SETTINGS_VALUE[] = "Settings";
|
|
const CHAR SAGESET_PARAM[] = " /SAGESET:";
|
|
|
|
BOOL GetAppNameFromPath(CHAR * pszFullPathName, CHAR * pszAppName);
|
|
BOOL AppNamesMatch(CHAR * pszCommand1, CHAR * pszCommand2);
|
|
BOOL fAppCantSupportMultipleInstances(CHAR * pszCmd);
|
|
BOOL AnsiToIntA(LPCSTR pszString, int * piRet);
|
|
HRESULT GetNextSageRunParam(HKEY hkSageApp, PINT pnSetNum);
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: DoSageSettings
|
|
//
|
|
// Synopsis: Invoke the Sage-aware app to alter its schedule settings.
|
|
//
|
|
// Arguments: [szCommand] - app name portion of command line
|
|
// [szSageRun] - "" or contains /SAGERUN switch on input.
|
|
// contains /SAGERUN switch on output if app is
|
|
// sage-aware.
|
|
//
|
|
// Modifies: *[szSageRun]
|
|
//
|
|
// Returns: S_OK - application launched
|
|
// S_FALSE - application doesn't support sage settings
|
|
// E_*
|
|
//
|
|
// History: 3-06-1997 DavidMun Added [szNewArg] & [cchNewArg].
|
|
// 4-16-1997 DavidMun Take strings from UI instead of job
|
|
// object.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
DoSageSettings(
|
|
LPSTR szCommand,
|
|
LPSTR szSageRun)
|
|
{
|
|
int nSetNum = 0;
|
|
|
|
if (!IsSageAware(szCommand, szSageRun, &nSetNum))
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
|
|
//
|
|
// We are here because the user hit the "Settings..." button, so if the
|
|
// arguments don't include a sageset parameter, we need to add it.
|
|
//
|
|
|
|
if (!*szSageRun)
|
|
{
|
|
//
|
|
// IsSageAware set nSetNum to the next valid number to use.
|
|
//
|
|
|
|
wsprintf(szSageRun, "%s%u", SAGERUN_PARAM, nSetNum);
|
|
}
|
|
|
|
//
|
|
// Create a command line to invoke the sage-aware app with the sageset
|
|
// parameter.
|
|
//
|
|
|
|
CHAR szSetCommand[MAX_PATH];
|
|
CHAR szSetParams[MAX_PATH];
|
|
|
|
lstrcpyn(szSetCommand, szCommand, MAX_PATH);
|
|
lstrcpy(szSetParams, SAGESET_PARAM);
|
|
|
|
wsprintf(&szSetParams[lstrlen(szSetParams)], "%u", nSetNum);
|
|
|
|
if (!GetAppNameFromPath(szSetCommand, NULL)) //got a path?
|
|
{
|
|
//
|
|
// GetAppNameFromPath returns FALSE if szSetCommand is not a full
|
|
// path. So, see if the app has registered a path. If it has,
|
|
// GetAppPath places the full path name in szFullyQualified.
|
|
//
|
|
CHAR szFullyQualified[MAX_PATH];
|
|
|
|
GetAppPathInfo(szSetCommand,
|
|
szFullyQualified,
|
|
MAX_PATH,
|
|
NULL,
|
|
0);
|
|
|
|
if (*szFullyQualified)
|
|
{
|
|
lstrcpy(szSetCommand, szFullyQualified);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Prefix the system path with the directories listed by the application
|
|
// in the app paths key.
|
|
//
|
|
|
|
BOOL fChangedPath;
|
|
LPSTR pszSavedPath;
|
|
|
|
fChangedPath = SetAppPath(szSetCommand, &pszSavedPath);
|
|
|
|
//
|
|
// Put the SageSet param onto the command line.
|
|
//
|
|
if (szSetParams[0] != ' ')
|
|
{
|
|
lstrcat(szSetCommand, " ");
|
|
}
|
|
lstrcat(szSetCommand, szSetParams);
|
|
|
|
DWORD dwErr = ERROR_SUCCESS;
|
|
STARTUPINFO sui;
|
|
PROCESS_INFORMATION pi;
|
|
ZeroMemory(&sui, sizeof(sui));
|
|
sui.cb = sizeof (STARTUPINFO);
|
|
|
|
if (CreateProcess(NULL,
|
|
szSetCommand,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
CREATE_NEW_CONSOLE |
|
|
CREATE_NEW_PROCESS_GROUP,
|
|
NULL,
|
|
NULL,
|
|
&sui,
|
|
&pi))
|
|
{
|
|
CloseHandle(pi.hThread);
|
|
CloseHandle(pi.hProcess);
|
|
}
|
|
else
|
|
{
|
|
dwErr = GetLastError();
|
|
ERR_OUT("DoSageSettings: CreateProcess", dwErr);
|
|
}
|
|
|
|
if (fChangedPath)
|
|
{
|
|
SetEnvironmentVariable(TEXT("PATH"), pszSavedPath);
|
|
delete [] pszSavedPath;
|
|
}
|
|
|
|
return (dwErr == ERROR_SUCCESS) ? S_OK : HRESULT_FROM_WIN32(dwErr);
|
|
}
|
|
|
|
//+--------------------------------------------------------------------------
|
|
//
|
|
// Function: IsSageAwareW
|
|
//
|
|
// Synopsis: Unicode wrapper for IsSageAware
|
|
//
|
|
// History: 10-27-1997 DavidMun Created
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
BOOL
|
|
IsSageAwareW(WCHAR *pwzCmd, WCHAR *pwzParams, int *pnSetNum)
|
|
{
|
|
HRESULT hr;
|
|
CHAR szCmd[MAX_PATH+1];
|
|
CHAR szParam[MAX_PATH+1];
|
|
BOOL fResult = FALSE;
|
|
|
|
do
|
|
{
|
|
hr = UnicodeToAnsi(szCmd, pwzCmd, ARRAYLEN(szCmd));
|
|
BREAK_ON_FAIL(hr);
|
|
|
|
hr = UnicodeToAnsi(szParam, pwzParams, ARRAYLEN(szParam));
|
|
BREAK_ON_FAIL(hr);
|
|
|
|
fResult = IsSageAware(szCmd, szParam, pnSetNum);
|
|
} while (0);
|
|
|
|
return fResult;
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: IsSageAware
|
|
//
|
|
// Synopsis: Does this app use Sage settings?
|
|
//
|
|
// Arguments: [pszCmd] - The task application name property.
|
|
// [pszParams] - The task parameters, can be NULL if pnSetNum is
|
|
// NULL.
|
|
// [pnSetNum] - the registry setting set number to return, can
|
|
// be NULL if not needed.
|
|
//
|
|
// Returns: TRUE if it does, FALSE otherwise.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
BOOL
|
|
IsSageAware(CHAR * pszCmd, const CHAR * pszParams, int * pnSetNum)
|
|
{
|
|
int index;
|
|
HKEY hSageKey;
|
|
HKEY hSageSubKey;
|
|
CHAR szSubKey[MAXPATH];
|
|
CHAR szSubKeyPath[MAXPATH];
|
|
CHAR szRegValue[MAXCOMMANDLINE];
|
|
long lRet;
|
|
CHAR *p;
|
|
BOOL fResult = FALSE;
|
|
DWORD cb;
|
|
|
|
if (pnSetNum != NULL)
|
|
{
|
|
*pnSetNum = 0;
|
|
}
|
|
|
|
if (RegOpenKey(HKEY_LOCAL_MACHINE, SAGE_KEY, &hSageKey))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
#define MAXSAGEPROGS 0xffffff
|
|
|
|
for (index = 0; index < MAXSAGEPROGS; index++)
|
|
{
|
|
//
|
|
// Examine each subkey of the Sage key.
|
|
//
|
|
lRet = RegEnumKey(hSageKey, index, szSubKey, MAXPATH);
|
|
|
|
if (lRet != ERROR_SUCCESS)
|
|
{
|
|
break;
|
|
}
|
|
|
|
lstrcpy(szSubKeyPath, SAGE_KEY);
|
|
lstrcat(szSubKeyPath, "\\");
|
|
lstrcat(szSubKeyPath, szSubKey);
|
|
|
|
if (RegOpenKey(HKEY_LOCAL_MACHINE,
|
|
szSubKeyPath,
|
|
&hSageSubKey) != ERROR_SUCCESS)
|
|
{
|
|
continue; //no path
|
|
}
|
|
|
|
//
|
|
// Look for a match on the application name.
|
|
//
|
|
cb = MAXPATH;
|
|
if (RegQueryValueEx(hSageSubKey,
|
|
PROGRAM_VALUE,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)szRegValue,
|
|
&cb) != ERROR_SUCCESS)
|
|
{
|
|
RegCloseKey(hSageSubKey);
|
|
continue; //no path
|
|
}
|
|
|
|
schDebugOut((DEB_ITRACE,
|
|
"IsSageAware enum: pszCmd = %s, szRegValue = %s\n",
|
|
pszCmd, szRegValue));
|
|
|
|
RegCloseKey(hSageSubKey);
|
|
|
|
if (AppNamesMatch(pszCmd, szRegValue))
|
|
{
|
|
if (RegOpenKey(HKEY_LOCAL_MACHINE,
|
|
szSubKeyPath,
|
|
&hSageSubKey) != ERROR_SUCCESS)
|
|
{
|
|
continue; //no settings dialog
|
|
}
|
|
|
|
fResult = FALSE;
|
|
|
|
if (RegQueryValueEx(hSageSubKey,
|
|
SETTINGS_VALUE,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)szRegValue,
|
|
&cb) == ERROR_SUCCESS)
|
|
{
|
|
if (szRegValue[0] == 0)
|
|
{
|
|
RegCloseKey(hSageSubKey);
|
|
break; // this means don't allow sage settings
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RegCloseKey(hSageSubKey);
|
|
continue; //no settings registry key
|
|
}
|
|
|
|
// test for app that can't handle multiple instances
|
|
//
|
|
if (!fAppCantSupportMultipleInstances(pszCmd))
|
|
{
|
|
fResult = TRUE;
|
|
}
|
|
|
|
if (fResult && pnSetNum != NULL)
|
|
{
|
|
//
|
|
// Extract the set number from the parameters, if requested.
|
|
//
|
|
p = _tcsstr(pszParams, SAGERUN_PARAM);
|
|
|
|
if (p != NULL)
|
|
{
|
|
AnsiToIntA(p + lstrlen(SAGERUN_PARAM), pnSetNum);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// This job is for a sage-aware app which supports the
|
|
// SAGERUN switch, but it lacks that switch in its
|
|
// parameter property. Since the caller wants to know
|
|
// what value to use, generate one.
|
|
//
|
|
|
|
HRESULT hr = GetNextSageRunParam(hSageSubKey, pnSetNum);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
fResult = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
RegCloseKey(hSageSubKey);
|
|
break;
|
|
}
|
|
}
|
|
|
|
RegCloseKey(hSageKey);
|
|
|
|
return fResult;
|
|
}
|
|
|
|
|
|
|
|
const CHAR SET_PREFIX[] = "Set";
|
|
|
|
//+--------------------------------------------------------------------------
|
|
//
|
|
// Function: CreateSageRunKey
|
|
//
|
|
// Synopsis: Create a registry key with string representation of
|
|
// value [uiKey] for app [szSageAwareExe].
|
|
//
|
|
// Arguments: [szSageAwareExe] - app for which to create key
|
|
// [uiKey] - numeric representation of key name
|
|
//
|
|
// Returns: S_OK or E_FAIL
|
|
//
|
|
// History: 10-28-1997 DavidMun Created
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CreateSageRunKey(
|
|
LPCSTR szSageAwareExe,
|
|
UINT uiKey)
|
|
{
|
|
HRESULT hr = E_FAIL; // init for failure
|
|
int index;
|
|
HKEY hSageKey;
|
|
HKEY hSageSubKey;
|
|
CHAR szSubKey[MAXPATH];
|
|
CHAR szSubKeyPath[MAXPATH];
|
|
CHAR szRegValue[MAXCOMMANDLINE];
|
|
long lRet;
|
|
DWORD cb;
|
|
|
|
if (RegOpenKey(HKEY_LOCAL_MACHINE, SAGE_KEY, &hSageKey))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
for (index = 0; index < MAXSAGEPROGS; index++)
|
|
{
|
|
//
|
|
// Examine each subkey of the Sage key.
|
|
//
|
|
lRet = RegEnumKey(hSageKey, index, szSubKey, MAXPATH);
|
|
|
|
if (lRet != ERROR_SUCCESS)
|
|
{
|
|
break;
|
|
}
|
|
|
|
lstrcpy(szSubKeyPath, SAGE_KEY);
|
|
lstrcat(szSubKeyPath, "\\");
|
|
lstrcat(szSubKeyPath, szSubKey);
|
|
|
|
if (RegOpenKey(HKEY_LOCAL_MACHINE,
|
|
szSubKeyPath,
|
|
&hSageSubKey) != ERROR_SUCCESS)
|
|
{
|
|
continue; //no path
|
|
}
|
|
|
|
//
|
|
// Look for a match on the application name.
|
|
//
|
|
cb = MAXPATH;
|
|
if (RegQueryValueEx(hSageSubKey,
|
|
PROGRAM_VALUE,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)szRegValue,
|
|
&cb) != ERROR_SUCCESS)
|
|
{
|
|
RegCloseKey(hSageSubKey);
|
|
continue; //no path
|
|
}
|
|
|
|
RegCloseKey(hSageSubKey);
|
|
|
|
if (AppNamesMatch((LPSTR)szSageAwareExe, szRegValue))
|
|
{
|
|
if (RegOpenKey(HKEY_LOCAL_MACHINE,
|
|
szSubKeyPath,
|
|
&hSageSubKey) != ERROR_SUCCESS)
|
|
{
|
|
continue; //no settings dialog
|
|
}
|
|
|
|
CHAR szKeyName[MAX_PATH];
|
|
HKEY hkNewKey = NULL;
|
|
|
|
wsprintf(szKeyName, "%s%u", SET_PREFIX, uiKey);
|
|
lRet = RegCreateKey(hSageSubKey, szKeyName, &hkNewKey);
|
|
|
|
if (hkNewKey)
|
|
{
|
|
RegCloseKey(hkNewKey);
|
|
}
|
|
RegCloseKey(hSageSubKey);
|
|
|
|
if (lRet == ERROR_SUCCESS)
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
schDebugOut((DEB_ERROR,
|
|
"CreateSageRunKey: RegCreateKey %uL",
|
|
lRet));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
RegCloseKey(hSageKey);
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
|
|
//+--------------------------------------------------------------------------
|
|
//
|
|
// Function: GetNextSageRunParam
|
|
//
|
|
// Synopsis: Fill *[pnSetNum] with the next <n> value to use for a new
|
|
// Set<n> subkey under [hSageAppSubKey].
|
|
//
|
|
// Arguments: [hSageAppSubKey] - handle to app's key under SAGE key
|
|
// [pnSetNum] - filled with next number to use
|
|
//
|
|
// Returns: HRESULT
|
|
//
|
|
// Modifies: *[pnSetNum]
|
|
//
|
|
// History: 3-06-1997 DavidMun Created
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
GetNextSageRunParam(
|
|
HKEY hSageAppSubKey,
|
|
PINT pnSetNum)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ULONG idxKey;
|
|
|
|
*pnSetNum = 0;
|
|
|
|
for (idxKey = 0; TRUE; idxKey++)
|
|
{
|
|
LONG lr;
|
|
CHAR szSubKey[MAX_PATH + 1];
|
|
|
|
lr = RegEnumKey(hSageAppSubKey, idxKey, szSubKey, ARRAYLEN(szSubKey));
|
|
|
|
//
|
|
// Quit on error, including end of subkeys
|
|
//
|
|
|
|
if (lr != ERROR_SUCCESS)
|
|
{
|
|
if (lr != ERROR_NO_MORE_ITEMS)
|
|
{
|
|
hr = E_FAIL;
|
|
schDebugOut((DEB_ERROR,
|
|
"GetNextSageRunParam: RegEnumKey %uL",
|
|
lr));
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Ignore this key if it doesn't start with "Set"
|
|
//
|
|
|
|
CHAR szSet[ARRAYLEN(SET_PREFIX)];
|
|
lstrcpyn(szSet, szSubKey, ARRAYLEN(SET_PREFIX));
|
|
|
|
if (lstrcmpi(szSet, SET_PREFIX))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Get the numeric value after the prefix. If there's no valid
|
|
// number, ignore this key.
|
|
//
|
|
|
|
BOOL fNumber;
|
|
INT iSetN;
|
|
|
|
fNumber = AnsiToIntA(szSubKey + ARRAYLEN(SET_PREFIX) - 1, &iSetN);
|
|
|
|
if (!fNumber)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// If the number matches or exceeds the one we plan to use, make ours
|
|
// larger.
|
|
//
|
|
|
|
if (iSetN >= *pnSetNum)
|
|
{
|
|
*pnSetNum = iSetN + 1;
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: GetAppNameFromPath
|
|
//
|
|
// Synopsis:
|
|
//
|
|
// Arguments: [pszFullPathName] - full or partial path
|
|
// [pszAppName] - buffer for app name, may be NULL
|
|
//
|
|
// Returns: TRUE - [pszFullPathName] contained slashes
|
|
// FALSE - [pszFullPathName] didn't contain slashes
|
|
//
|
|
// Modifies: *[pszAppName]
|
|
//
|
|
// History: 10-25-96 DavidMun Made DBCS safe
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL GetAppNameFromPath(CHAR * pszFullPathName, CHAR * pszAppName)
|
|
{
|
|
LPSTR pszLastSlash;
|
|
|
|
pszLastSlash = _tcsrchr(pszFullPathName, '\\');
|
|
|
|
if (!pszAppName)
|
|
{
|
|
return pszLastSlash != NULL;
|
|
}
|
|
|
|
if (pszLastSlash)
|
|
{
|
|
lstrcpy(pszAppName, pszLastSlash + 1);
|
|
}
|
|
else
|
|
{
|
|
lstrcpy(pszAppName, pszFullPathName);
|
|
}
|
|
|
|
schDebugOut((DEB_ITRACE, "GetAppNameFromPath app name: %s\n", pszAppName));
|
|
|
|
if (pszAppName[0] != '"')
|
|
{
|
|
LPSTR pszQuote = _tcschr(pszAppName, '"');
|
|
|
|
if (pszQuote)
|
|
{
|
|
*pszQuote = '\0';
|
|
}
|
|
}
|
|
return pszLastSlash != NULL;
|
|
}
|
|
|
|
BOOL AppNamesMatch(CHAR *pszCommand1, CHAR *pszCommand2)
|
|
{
|
|
CHAR short1[MAXPATH];
|
|
CHAR short2[MAXPATH];
|
|
|
|
GetAppNameFromPath(pszCommand1, short1);
|
|
GetAppNameFromPath(pszCommand2, short2);
|
|
|
|
if (lstrcmpi(short1, short2) == 0)
|
|
{
|
|
return(TRUE);
|
|
}
|
|
return(FALSE);
|
|
}
|
|
|
|
BOOL fAppCantSupportMultipleInstances(CHAR * pszCmd)
|
|
{
|
|
if (AppNamesMatch(pszCmd, "SCANDSKW.EXE") ||
|
|
AppNamesMatch(pszCmd, "DEFRAG.EXE") ||
|
|
AppNamesMatch(pszCmd, "CMPAGENT.EXE"))
|
|
{
|
|
if (FindWindow("ScanDskWDlgClass", NULL))
|
|
return TRUE;
|
|
if (FindWindow("MSDefragWClass1", NULL))
|
|
return TRUE;
|
|
if (FindWindow("MSExtraPakWClass1", NULL))
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: ScottH's version of atoi. Supports hexadecimal too.
|
|
|
|
If this function returns FALSE, *piRet is set to 0.
|
|
|
|
Returns: TRUE if the string is a number, or contains a partial number
|
|
FALSE if the string is not a number
|
|
*/
|
|
BOOL AnsiToIntA(LPCSTR pszString, int * piRet)
|
|
{
|
|
#define InRange(id, idFirst, idLast) \
|
|
((UINT)(id-idFirst) <= (UINT)(idLast-idFirst))
|
|
#define IS_DIGIT(ch) InRange(ch, '0', '9')
|
|
|
|
BOOL bRet;
|
|
int n;
|
|
BOOL bNeg = FALSE;
|
|
LPCSTR psz;
|
|
LPCSTR pszAdj;
|
|
|
|
// Skip leading whitespace
|
|
//
|
|
for (psz = pszString;
|
|
*psz == ' ' || *psz == '\n' || *psz == '\t';
|
|
psz = NextChar(psz))
|
|
;
|
|
|
|
// Determine possible explicit signage
|
|
//
|
|
if (*psz == '+' || *psz == '-')
|
|
{
|
|
bNeg = (*psz == '+') ? FALSE : TRUE;
|
|
psz++;
|
|
}
|
|
|
|
// Or is this hexadecimal?
|
|
//
|
|
pszAdj = NextChar(psz);
|
|
if (*psz == '0' && (*pszAdj == 'x' || *pszAdj == 'X'))
|
|
{
|
|
// Yes
|
|
|
|
// (Never allow negative sign with hexadecimal numbers)
|
|
bNeg = FALSE;
|
|
psz = NextChar(pszAdj);
|
|
|
|
pszAdj = psz;
|
|
|
|
// Do the conversion
|
|
//
|
|
for (n = 0; ; psz = NextChar(psz))
|
|
{
|
|
if (IS_DIGIT(*psz))
|
|
n = 0x10 * n + *psz - '0';
|
|
else
|
|
{
|
|
CHAR ch = *psz;
|
|
int n2;
|
|
|
|
if (ch >= 'a')
|
|
ch -= 'a' - 'A';
|
|
|
|
n2 = ch - 'A' + 0xA;
|
|
if (n2 >= 0xA && n2 <= 0xF)
|
|
n = 0x10 * n + n2;
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Return TRUE if there was at least one digit
|
|
bRet = (psz != pszAdj);
|
|
}
|
|
else
|
|
{
|
|
// No
|
|
pszAdj = psz;
|
|
|
|
// Do the conversion
|
|
for (n = 0; IS_DIGIT(*psz); psz = NextChar(psz))
|
|
n = 10 * n + *psz - '0';
|
|
|
|
// Return TRUE if there was at least one digit
|
|
bRet = (psz != pszAdj);
|
|
}
|
|
|
|
*piRet = bNeg ? -n : n;
|
|
|
|
return bRet;
|
|
}
|
|
|