Leaked source code of windows server 2003
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.
 
 
 
 
 
 

1338 lines
37 KiB

#pragma warning(disable:4786)
#include <string>
#include <map>
#include <vector>
#include <stdio.h>
#include <algorithm>
#include "windows.h"
#include "io.h"
#include <limits.h>
#define NUMBER_OF(x) (sizeof(x)/sizeof((x)[0]))
#include <assert.h>
#if defined(_WIN64)
typedef unsigned __int64 ULONG_PTR;
#else
typedef unsigned long ULONG_PTR;
#endif
struct tagACTCTXW;
typedef const tagACTCTXW* PCACTCTXW;
typedef std::string::size_type size_type;
// we also wrap user and loadlibrary
const char Namespace[] = "SideBySide";
const char NamespaceComCtl[] = "SideBySideCommonControls";
#define NAMESPACE "SideBySide"
#define NAMESPACE_COMCTL "SideBySideCommonControls"
/*
enum ETokenType
{
ttComment,
ttChar,
ttIdentifer
};
class CToken
{
public:
ETokenType m_etype;
std::string m_string;
};
enum EType
{
tVoid,
tBool,
tInt,
tHRESULT,
tOther
};
*/
class CType
{
public:
//EType m_etype;
std::string m_string; // right from a CToken, we ignore the multiple token case for now
};
class CParameter
{
public:
CType m_type;
std::string m_name;
};
std::string BaseFunctions[] =
{
"LoadLibrary"
};
std::string UserFunctions[] =
{
"RegisterClass",
"RegisterClassEx",
"CreateWindow",
"CreateWindowEx",
"GetClassInfo",
"GetClassInfoEx",
"DialogBoxParam",
"DialogBoxIndirectParam",
"CreateDialogIndirect",
"CreateDialogIndirectParam",
"CreateDialogParam"
};
#define BEGIN(x) x
#define END(x) ((x) + sizeof(x) / sizeof((x)[0]))
std::string AlsoNoFusionFunctions[] =
{
//"CreateDialogIndirectT",
//"CreateDialogParamT",
"CreateWindowExT",
//"CreateWindowT",
"CreateDialogIndirectParamT",
"CreatePropertySheetPageT"
};
const static char prefix[]=
"\n\
#include \"windows.h\"\n\
\n\
HMODULE g_" NAMESPACE_COMCTL "DllHandle;\n\
\n\
DWORD " NAMESPACE_COMCTL "GetLastError()\n\
{\n\
DWORD Error = GetLastError();\n\
if (Error == NO_ERROR)\n\
{\n\
Error = -1;//ERROR_UNSUCCESSFUL;\n\
}\n\
return Error;\n\
}\n\
\n\
\n\
HANDLE g_" NAMESPACE_COMCTL "ActCtx;\n\
\n\
BOOL\n\
WINAPI\n\
" NAMESPACE_COMCTL "DeactivateActCtx_DownLevel(\n\
DWORD dwFlags,\n\
ULONG_PTR ulCookie\n\
)\n\
{\n\
return TRUE;\n\
}\n\
\n\
BOOL\n\
WINAPI\n\
" NAMESPACE_COMCTL "DeactivateActCtx(\n\
DWORD dwFlags,\n\
ULONG_PTR ulCookie\n\
)\n\
{\n\
typedef BOOL (WINAPI* PFN)(DWORD dwFlags, ULONG_PTR ulCookie);\n\
static PFN pfn;\n\
BOOL Success = FALSE;\n\
\n\
if (pfn == NULL)\n\
{\n\
HMODULE Kernel32 = GetModuleHandleW(L\"Kernel32.dll\");\n\
if (Kernel32 == NULL) // this is fatal, even downlevel\n\
goto Exit;\n\
pfn = (PFN)GetProcAddress(Kernel32, \"DeactivateActCtx\");\n\
if (pfn == NULL)\n\
pfn = " NAMESPACE_COMCTL "DeactivateActCtx_DownLevel;\n\
}\n\
Success = pfn(dwFlags, ulCookie);\n\
Exit:\n\
return Success;\n\
}\n\
\n\
BOOL\n\
WINAPI\n\
" NAMESPACE_COMCTL "ActivateActCtx_DownLevel(\n\
HANDLE hActCtx,\n\
ULONG_PTR *lpCookie\n\
)\n\
{\n\
return TRUE;\n\
}\n\
\n\
BOOL\n\
WINAPI\n\
" NAMESPACE_COMCTL "ActivateActCtx(\n\
HANDLE hActCtx,\n\
ULONG_PTR *lpCookie\n\
)\n\
{\n\
typedef BOOL (WINAPI* PFN)(HANDLE hActCtx, ULONG_PTR *lpCookie);\n\
static PFN pfn;\n\
BOOL Success = FALSE;\n\
\n\
if (pfn == NULL)\n\
{\n\
HMODULE Kernel32 = GetModuleHandleW(L\"Kernel32.dll\");\n\
if (Kernel32 == NULL) // this is fatal, even downlevel\n\
goto Exit;\n\
pfn = (PFN)GetProcAddress(Kernel32, \"ActivateActCtx\");\n\
if (pfn == NULL)\n\
pfn = " NAMESPACE_COMCTL "ActivateActCtx_DownLevel;\n\
}\n\
Success = pfn(hActCtx, lpCookie);\n\
Exit:\n\
return Success;\n\
}\n\
\n\
BOOL\n\
WINAPI\n\
" NAMESPACE_COMCTL "CreateActCtxW_DownLevel(\n\
PCACTCTXW pActCtx\n\
)\n\
{\n\
return TRUE;\n\
}\n\
\n\
BOOL\n\
WINAPI\n\
" NAMESPACE_COMCTL "CreateActCtxW(\n\
PCACTCTXW pActCtx\n\
)\n\
{\n""\
typedef BOOL (WINAPI* PFN)(PCACTCTXW pActCtx);\n\
static PFN pfn;\n\
BOOL Success = FALSE;\n\
\n\
if (pfn == NULL)\n\
{\n\
HMODULE Kernel32 = GetModuleHandleW(L\"Kernel32.dll\");\n\
if (Kernel32 == NULL) // this is fatal, even downlevel\n\
goto Exit;\n\
pfn = (PFN)GetProcAddress(Kernel32, \"CreateActCtxW\");\n\
if (pfn == NULL)\n\
pfn = " NAMESPACE_COMCTL "CreateActCtxW_DownLevel;\n\
}\n\
Success = pfn(pActCtx);\n\
Exit:\n\
return Success;\n\
}\n\
\n\
/* Requires NT4.0, Win98. SideBySide functionality is currently only on Whistler,\n\
and will fail before it tries this. */\n\
PVOID "NAMESPACE_COMCTL"InterlockedCompareExchangePointer(PVOID* Dest, PVOID Exch, PVOID Comperand)\n\
{\n\
#if defined(_X86_)\n\
typedef PVOID (WINAPI* PFN)(PVOID*, PVOID, PVOID);\n\
static PFN pfn;\n\
/* Note, the export has a different name; this is correct. */\n\
pfn = (PFN)GetProcAddress(GetModuleHandle(TEXT(\"Kernel32.dll\"), \"InterlockedCompareExchange\"));\n\
/* UNDONE Error handling here.. */\n\
return pfn(Dest, Exch, Comperand);\n\
#else\n\
/* always available on IA64, and most other processors */\n\
return InterlockedCompareExchangePointer(Dest, Exch, Comperand);\n\
#endif\n\
}\n\
\n\
HANDLE g_" NAMESPACE_COMCTL "ActCtxHandle = INVALID_HANDLE_VALUE;\n\
\n\
#if defined(__cplusplus)\n\
extern \"C\"\n\
#endif\n\
int __ImageBase; /* requires VC6 linker */\n\
\n\
BOOL\n\
WINAPI\n\
" NAMESPACE_COMCTL "CreateMyActCtx(\n\
)\n\
{\n\
ACTCTX ActCtx;\n\
HANDLE LocalActCtxHandle = INVALID_HANDLE_VALUE;\n\
static DWORD Error;\n\
PTSTR ModuleFileName = NULL;\n\
DWORD ModuleNameBufferSize = 32;\n\
DWORD ModuleNameSize;\n\
BOOL Success = FALSE;\n\
BOOL First = TRUE;\n\
\n\
if (Error != 0)\n\
{\n\
SetLastError(Error);\n\
goto Exit;\n\
}\n\
if (g_"NAMESPACE_COMCTL"ActCtxHandle == INVALID_HANDLE_VALUE)\n\
{\n\
do\n\
{\n\
if (ModuleName == NULL)\n\
{\n\
ModuleFileName = (PTSTR)HeapAlloc(GetProcessHeap(), 0, ModuleNameBufferSize * sizeof(*ModuleName));\n\
if (ModuleName == NULL)\n\
{\n\
Error = ERROR_NOT_ENOUGH_MEMORY;\n\
goto Exit;\n\
}\n\
}\n\
else\n\
{\n\
PTSTR ModuleNameLonger\n\
ModuleBufferSize *= 2;\n\
ModuleNameLonger = (PSTR)HeapReAlloc(GetProcessHeap(), 0, ModuleFileName, ModuleBufferSize * sizeof(*ModuleNameLonger));\n\
if (ModuleNameLonger == NULL)\n\
{\n\
Error = ERROR_NOT_ENOUGH_MEMORY;\n\
goto Exit;\n\
}\n\
ModuleFileName = ModuleNameLonger;\n\
}\n\
ModuleFileName[ModuleNameBufferSize - 2] = 0;\n\
if (!GetModuleFileName(&__ImageBase, ModuleFileName, ModuleNameBufferSize))\n\
{\n\
Error = " NAMESPACE_COMCTL "GetLastError();\n\
goto Exit;\n\
}\n\
} while (ModuleName[ModuleNameBufferSize - 2] != 0)\n\
ActCtx.cbSize = sizeof(ActCtx);\n\
ActCtx.dwFlags = 0;\n\
ActCtx.lpSource = ModuleName;\n\
LocalActCtxHandle = NAMESPACE_COMCTL CreateActCtx(&ActCtx);\n\
if (LocalActCtxHandle == INVALID_HANDLE_VALUE)\n\
goto Exit;\n\
NAMESPACE_COMCTL InterlockedCompareExchangePointer((PVOID*)&g_"NAMESPACE_COMCTL"ActCtxHandle, LocalActCtxHandle, INVALID_HANDLE_VALUE);\n\
}\n\
Success = TRUE;\n\
Exit:\n\
/* If out of memory, let it be retried. */\n\
if (Error != ERROR_NOT_ENOUGH_MEMORY)\n\
Initialized = TRUE;\n\
if (ModuleFileName != NULL)\n\
{\n\
DWORD LastError = GetLastError();\n\
HeapFree(GetProcessHeap(), 0, ModuleFileName);\n\
SetLastError(LastError);\n\
}\n\
\n\
return Success;\n\
}\n\
\n\
BOOL " NAMESPACE_COMCTL "DelayLoad(HMODULE* Module, DWORD* Error)\n\
{\n\
BOOL Success = FALSE;\n\
ULONG_PTR Cookie;\n\
BOOL ActivateSuccess = FALSE;\n\
\n\
if (*Module != NULL)\n\
{\n\
Success = TRUE;\n\
goto Exit;\n\
}\n\
if (*Error != 0)\n\
{\n\
SetLastError(*Error);\n\
goto Exit;\n\
}\n\
Success = " NAMESPACE_COMCTL "ActivateActCtx(g_" NAMESPACE_COMCTL "ActCtxHandle, &Cookie);\n\
if (!Success)\n\
{\n\
*Error = " NAMESPACE_COMCTL "GetLastError();\n\
goto Exit;\n\
}\n\
__try\n\
{\n\
*Module = LoadLibrary(TEXT(\"comctl32.dll\"));\n\
if (*Module == NULL)\n\
{\n\
*Error = " NAMESPACE_COMCTL "GetLastError();\n\
goto Exit;\n\
}\n\
}\n\
__finally\n\
{\n\
if (AbnormalTermination())\n\
{\n\
DWORD LastError = GetLastError();\n\
" NAMESPACE_COMCTL "DeactivateActCtx(0, Cookie);\n\
SetLastError(LastError);\n\
}\n\
else\n\
{\n\
if (!" NAMESPACE_COMCTL "DeactivateActCtx(0, Cookie))\n\
{\n\
*Error = " NAMESPACE_COMCTL "GetLastError();\n\
goto Exit;\n\
}\n\
}\n\
}\n\
Success = TRUE;\n\
Exit:\n\
return Success;\n\
}\n\
\n" "\n\
\n\
/* TODO wrapping up multiple const-static parameters in a struct is a code size win */\n\
#if 0\n\
typedef "NAMESPACE_COMCTL"GetProcAddressParameters\n\
{\n\
PCSTR Name;\n\
FARPROC* Address;\n\
DWORD* Error;\n\
} "NAMESPACE_COMCTL"GetProcAddressParameters\n\
#endif\n\
\n\
BOOL " NAMESPACE_COMCTL "GetProcAddress(PCSTR ProcedureName, FARPROC* ppfn, DWORD* GetProcAddressError)\n\
/*\n\
We have an error per function, as well as one overriding error, because\n\
we can error on LoadLibrary or we could succeed LoadLibrary but then\n\
error on some GetProcAddresses. Error on LoadLibrary propatated to all GetProcAddresses\n\
with no retries. Errors on GetProcAddresses are isolated to GetProcAddresses.\n\
*/\n\
{\n\
static HMODULE Module;\n\
static DWORD LoadLibraryError;\n\
BOOL Success = FALSE;\n\
\n\
if (*ppfn != NULL)\n\
{\n\
Success = TRUE;\n\
goto Exit;\n\
}\n\
if (*GetProcAddressError != 0)\n\
{\n\
SetLastError(*GetProcAddressError);\n\
goto Exit;\n\
}\n\
if (LoadLibraryError != 0)\n\
{\n\
SetLastError(LoadLibraryError);\n\
*GetProcAddressError = LoadLibraryError;\n\
goto Exit;\n\
}\n\
if (!" NAMESPACE_COMCTL "DelayLoad(&Module, &LoadLibraryError))\n\
{\n\
*GetProcAddressError = LoadLibraryError;\n\
goto Exit;\n\
}\n\
if ((*ppfn = GetProcAddress(Module, ProcedureName)) == NULL)\n\
{\n\
*GetProcAddressError = " NAMESPACE_COMCTL "GetLastError();\n\
/*\n\
Don't touch LoadLibraryError for GetProcAddress.\n\
*/\n\
goto Exit;\n\
}\n\
Success = TRUE;\n\
Exit:\n\
return Success;\n\
}\n\
";
#if 0 // FUTURE We should make this be multipass, it's easier.
class CStreamBuffer : public IStream
{
public:
CStreamBuffer(IStream* Stream) : m_Stream(Stream) { }
STDMETHOD(Read)(void* pv, ULONG cb, ULONG* pcbRead)
{
ULONG cbRead = 0;
HRESULT hr;
if (pcbRead != NULL)
*pcbRead = 0;
if (cb == 0)
{
hr = S_OK;
goto Exit;
}
if (FAILED(hr = FlushWriteBuffer()))
goto Exit;
if (m_BufferSize != 0)
{
cbRead = MIN(cb, m_BufferSize);
CopyMemory(pv, m_Buffer + m_BufferPosition, cbRead);
cb -= cbRead;
m_BufferPosition += cbRead;
m_BufferSize -= cbRead;
}
if (cb == 0)
{
hr = S_OK;
goto Exit;
}
/*
depending on the client, we could stop here with a partial read
for more compatibility, we will push ahead and fill the buffer in order
to give them all that they asked for
*/
if (cb >= NUMBER_OF(m_Buffer))
{
hr = m_Stream->Read(pv, cb, pcbRead);
if (pcbRead != NULL)
cbRead += *pcbRead;
goto Exit;
}
if (FAILED(hr = FillReadBuffer()))
return hr;
CopyMemory(pv, reinterpret_cast<PBYTE>(m_Buffer) + m_BufferPosition, cb);
m_BufferPosition += cb;
m_BufferSize -= cb;
cbRead += cb;
hr = S_OK;
Exit:
if (pcbRead != NULL)
*pcbRead = cbRead;
return hr;
}
HRESULT FillReadBuffer()
{
ULONG cb;
m_fReadBuffer = true;
HRESULT hr = m_Stream->Read(m_Buffer, NUMBER_OF(m_Buffer), &cb);
m_BufferPosition = 0;
m_BufferSize = cb;
return hr;
}
HRESULT FlushWriteBuffer()
{
ULONG cb;
if (m_fWriteBuffer && m_BufferSize != 0)
{
HRESULT hr = m_Stream->Write(m_Buffer, m_BufferSize, &cb);
m_BufferPosition = 0;
m_BufferSize = cb;
return hr;
}
return S_OK;
}
IStream* m_Stream;
BYTE m_Buffer[4096];
ULONG m_BufferPosition;
ULONG m_BufferSize;
bool m_fReadBuffer;
bool m_fWriteBuffer;
};
#endif // FUTURE
class CFunction
{
public:
CFunction()
{
Clear();
}
void PrinDefine(FILE* File, const char* FromPrefix, const char* ToPrefix) const
{
fprintf(File, "#define %s%s %s%s\n", FromPrefix, m_name.c_str(), ToPrefix, m_name.c_str());
}
void PrintStub(FILE* File) const
{
if (m_isComctl)
{
fprintf(
File,
"%s\n"
"WINAPI\n"
"SideBySide%s(%s)\n"
"{\n"
" static DWORD Error\n"
" typedef %s (WINAPI* PFN)(%s);\n"
" static PFN pfn;\n"
" if (pfn == NULL)\n"
" " NAMESPACE_COMCTL "GetProcAddress(\"%s\", (FARPROC*)&pfn, &Error);\n"
" if (pfn != NULL)\n"
" return pfn(%s);\n"
" SetLastError(Error);\n"
" return %s;\n"
"}\n\n",
m_returnType.m_string.c_str(),
m_name.c_str(),
m_parameterTypesNamesString.c_str(),
m_returnType.m_string.c_str(),
m_parameterTypesNamesString.c_str(),
m_name.c_str(),
m_parameterNamesString.c_str(),
m_error.c_str()
);
}
else
{
fprintf(
File,
"%s\n"
"WINAPI\n"
"SideBySide%s(%s)\n"
"{\n"
" ULONG_PTR ActCtxCookie;\n"
" %s %s %s;
" BOOL ActivateActCtxSuccess;
"
" ActivateActCtxSuccess
" return %s(%s);\n"
" SetLastError(Error);\n"
" return %s;\n"
"}\n\n",
m_returnType.m_string.c_str(),
m_name.c_str(),
m_parameterTypesNamesString.c_str(),
m_returnType.m_string.c_str(),
m_parameterTypesNamesString.c_str(),
m_name.c_str(),
m_parameterNamesString.c_str(),
m_error.c_str()
);
}
}
void FormParameterStrings()
{
m_parameterTypesNamesString.erase();
m_parameterNamesString.erase();
bool first = true;
if (m_parameters.begin() != m_parameters.end())
{
for (CParameters::const_iterator i = m_parameters.begin() ; i != m_parameters.end() ; ++i)
{
if (!first)
{
m_parameterTypesNamesString += ", ";
m_parameterNamesString += ", ";
}
first = false;
m_parameterTypesNamesString += i->m_type.m_string;
m_parameterTypesNamesString += " ";
m_parameterTypesNamesString += i->m_name.c_str();
m_parameterNamesString += i->m_name.c_str();
}
m_isVoid = false;
}
else
{
m_parameterTypesNamesString = "void";
m_isVoid = true;
}
}
void Clear()
{
m_isVoid = false;
m_isBase = false;
m_isUser = false;
m_isComctl = false;
m_parameters.clear();
m_parameterTypesNamesString.erase();
m_parameterNamesString.erase();
m_name.erase();
m_error.erase();
m_returnType.m_string.erase();
// temporary, until we finish annotating commctrl.h
m_error = "0";
}
CType m_returnType;
typedef std::vector<CParameter> CParameters;
CParameters m_parameters;
std::string m_parameterTypesNamesString;
std::string m_parameterNamesString;
std::string m_name;
std::string m_nameGeneric;
std::string m_error;
bool m_isVoid;
bool m_isBase; // WINBASEAPI (just LoadLibrary)
bool m_isUser; // WINUSERAPI
bool m_isComctl; // WINCOMMCTRLAPI
};
std::string GetEnvironmentVariable(const std::string& s)
{
char buffer[4000];
buffer[0] = 0;
GetEnvironmentVariableA(s.c_str(), buffer, NUMBER_OF(buffer));
return buffer;
}
std::vector<std::string> SplitStringOnChar(std::string s, char c)
{
std::vector<std::string> result;
#if 0
std::string::const_iterator i;
std::string::const_iterator j;
for (i = s.begin() ; i != s.end() ; ++i)
{
i += findf_first
}
#endif
return result;
}
void ThrowErrno()
{
throw errno;
}
class CStdioFile
{
public:
CStdioFile(FILE* file = NULL) : m_file(file) { }
operator FILE*()
{
return m_file;
}
void operator=(FILE* file)
{
Close();
m_file = file;
}
FILE* operator->()
{
return m_file;
}
~CStdioFile()
{
Close();
}
void Close()
{
FILE* file = m_file;
m_file = NULL;
if (file != NULL)
{
if (fclose(file) != 0)
{
ThrowErrno();
}
}
}
FILE* m_file;
};
class CMakeSideBySideCommonControls
{
public:
CMakeSideBySideCommonControls() { }
std::string m_path;
std::string m_string;
//std::vector<CToken> m_tokens;
typedef std::vector<CFunction> CFunctions;
CFunctions m_functions;
void OpenAndRead(const std::string& filename)
{
std::string ntDrive = GetEnvironmentVariable("_NTDRIVE");
std::string ntRoot = GetEnvironmentVariable("_NTROOT");
std::string include = GetEnvironmentVariable("include"); // future
std::vector<std::string> includes = SplitStringOnChar(include, ';'); // future
#if 0
if (ntDrive == "")
{
ntDrive = "Z:";
}
if (ntRoot == "")
{
ntRoot = "nt";
}
#else
if (ntDrive == "")
{
ntDrive = "x:";
}
if (ntRoot == "")
{
ntRoot = "sh1";
}
#endif
std::string directory = ntDrive + "\\" + ntRoot + "\\public\\sdk\\inc\\";
m_path = directory + filename;
CStdioFile file = fopen(m_path.c_str(), "rb");
if (file == NULL)
ThrowErrno();
__int64 size = _filelengthi64(_fileno(file));
if (size == -1)
ThrowErrno();
//size_type previousSize = m_string.size();
size_type previousSize = 0;
m_string.resize(previousSize + static_cast<size_type>(size));
size_t elementsRead = fread(&m_string[previousSize], sizeof(char), size, file);
if (elementsRead != size)
ThrowErrno();
m_string.append("\n\n");
}
static void FindAndReplaceChar(std::string& s, char from, char to)
{
for (std::string::iterator i = s.begin(); i != s.end() ; ++i)
{
if (*i == from)
*i = to;
}
}
#define STRIPCOMMENTS_SLASHSLASH 0x000001
#define STRIPCOMMENTS_SLASHSTAR 0x000002
#define STRIPCOMMENTS_SLASHSLASH_UNAWARE 0x000004
#define STRIPCOMMENTS_SLASHSTAR_UNAWARE 0x000008
static void StripComments(int flags, std::string& s)
/*
We generally want to be "aware" of both types so that we don't
strip nested comments. Consider the comments that follow.
*/
// /* slash star in slsh slash */
/* // slashslash
in slash star
*/
{
std::string t;
std::string::const_iterator i;
const std::string::const_iterator j = s.end();
std::string::const_iterator k;
bool closed = true;
t.reserve(s.size());
for (i = s.begin() ; closed && i != j && i + 1 != j; )
{
if (
((flags & STRIPCOMMENTS_SLASHSTAR) || (flags & STRIPCOMMENTS_SLASHSTAR_UNAWARE) == 0)
&& *i == '/'
&& *(i + 1) == '*'
)
{
closed = false;
for (k = i + 2 ; k != j && k + 1 != j && !(closed = (*k == '*' && *(k + 1) == '/')) ; ++k)
{
}
if (flags & STRIPCOMMENTS_SLASHSTAR)
t.append(1, ' ');
else
t.append(i, k + 2);
i = k + 2;
}
else if (
((flags & STRIPCOMMENTS_SLASHSLASH) || (flags & STRIPCOMMENTS_SLASHSLASH_UNAWARE) == 0)
&& *i == '/'
&& *(i + 1) == '/'
)
{
closed = false;
for (k = i + 2 ; k != j && !(closed = (*k == '\r' || *k == '\n')) ; ++k)
{
}
for ( ; k != j && *k == '\r' || *k == '\n' ; ++k)
{
}
if (flags & STRIPCOMMENTS_SLASHSLASH)
t.append(1, '\n');
else
t.append(i, k);
i = k;
}
if (closed && i != j)
t.append(1, *i++);
}
if (closed)
{
for ( ; i != j ; ++i )
{
t.append(1, *i);
}
}
s = t;
}
void ProcessDeclaration(CFunction function, const std::string& comment, std::string declaration)
{
size_type i = 0;
size_type j = 0;
const static char whitespaceCommaLparen[] = ",) \t\r\n";
const static char whitespace[] = " \t\r\n";
const static char beforeParamsDelims[] = " \t\n\r(";
const static char intracommaLparen[] = " \t\n\r*&"; // crude way to detect
// void F(char*) vs. void F(char x)
const static char commaLparen[] = ",)";
const static char identifierCharacters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
bool rparen = false;
bool end = false;
bool comma = false;
const size_type npos = std::string::npos;
//
// This way our ban on [] can be less strict.
// We ban [] because aren't smart about multi token types.
// We allow parameters like const char * foo, because commctrl does have parameters
// whose types are multiple tokens.
//
StripComments(STRIPCOMMENTS_SLASHSLASH | STRIPCOMMENTS_SLASHSTAR, declaration);
if (declaration.find_first_of("[]") != npos)
{
assert(declaration.find("WriteFileGather") != npos
|| declaration.find("ReadFileScatter") != npos
);
}
i = declaration.find_first_not_of(beforeParamsDelims, j);
j = declaration.find_first_of(beforeParamsDelims, i);
assert(declaration.substr(i, j - i) == "WINBASEAPI"
|| declaration.substr(i, j - i) == "WINUSERAPI"
|| declaration.substr(i, j - i) == "WINCOMMCTRLAPI"
);
i = declaration.find_first_not_of(beforeParamsDelims, j);
j = declaration.find_first_of(beforeParamsDelims, i);
if (
declaration.substr(i, j - i) == "DECLSPEC_NORETURN"
)
{
i = declaration.find_first_not_of(beforeParamsDelims, j);
j = declaration.find_first_of(beforeParamsDelims, i);
}
function.m_returnType.m_string.assign(declaration.substr(i, j != npos ? j - i : npos));
i = declaration.find_first_not_of(beforeParamsDelims, j);
j = declaration.find_first_of(beforeParamsDelims, i);
if (
declaration.substr(i, j - i) == "WINAPI"
|| declaration.substr(i, j - i) == "WINAPIV"
|| declaration.substr(i, j - i) == "NTAPI"
|| declaration.substr(i, j - i) == "_stcall"
|| declaration.substr(i, j - i) == "__stcall"
|| declaration.substr(i, j - i) == "_cdecl"
|| declaration.substr(i, j - i) == "__cdecl"
|| declaration.substr(i, j - i) == "APIENTRY"
|| declaration.substr(i, j - i) == "FASTCALL"
|| declaration.substr(i, j - i) == "_fastcall"
|| declaration.substr(i, j - i) == "__fastcall"
)
{
i = declaration.find_first_not_of(beforeParamsDelims, j);
j = declaration.find_first_of(beforeParamsDelims, i);
}
function.m_name.assign(declaration.substr(i, j != npos ? j - i : npos));
if ( *(function.m_name.end() - 1) == 'A'
|| *(function.m_name.end() - 1) == 'W'
)
{
function.m_nameGeneric.assign(function.m_name.begin(), function.m_name.end() - 1);
}
j += (j != npos);
//
// now split mainly on comma
//
while (
i != declaration.size()
&& j != declaration.size()
&& i != npos
&& j != npos
)
{
CParameter parameter;
i = declaration.find_first_not_of(whitespaceCommaLparen, j);
if (i == npos)
break;
j = declaration.find_first_of(commaLparen, i);
//
// temporary, we still might split off the name
//
parameter.m_type.m_string = declaration.substr(i, j != npos ? j - i : npos);
if ( parameter.m_type.m_string == "void"
|| parameter.m_type.m_string == "VOID"
|| parameter.m_type.m_string == ""
)
{
break;
}
#if 0
size_type splitParamNameFromType = parameter.m_type.m_string.find_last_of(typeTokenDelims);
if (splitParamNameFromType != npos)
{
// the parameter has a name
parameter.m_name = parameter.m_type.m_string.substr(splitParamNameFromType);
parameter.m_type.m_string.resize(splitParamNameFromType);
}
#else
size_type splitParamNameFromType = parameter.m_type.m_string.find_last_not_of(identifierCharacters);
if (splitParamNameFromType != npos)
{
// the parameter has a name
parameter.m_name = parameter.m_type.m_string.substr(splitParamNameFromType + 1);
parameter.m_type.m_string.resize(splitParamNameFromType);
}
#endif
else
{
char buffer[sizeof(int) * CHAR_BIT];
sprintf(buffer, "%d", static_cast<int>(function.m_parameters.size()));
parameter.m_name = "UnnamedParameter";
parameter.m_name += buffer;
}
j += (j != npos);
if (!comment.empty())
{
char functionFromComment[100];
char errorFromComment[100];
functionFromComment[0] = 0;
errorFromComment[0] = 0;
if (2 == sscanf(comment.c_str(), " @Function %s @Error %s ", functionFromComment, errorFromComment))
{
if (function.m_name != functionFromComment)
{
printf(
"comment /* %s */ has different function than expected %s in %s\n",
comment.c_str(),
function.m_name.c_str(),
m_path.c_str()
);
throw -1;
}
function.m_error = errorFromComment;
}
}
function.m_parameters.push_back(parameter);
}
if (function.m_name == "LoadLibraryA")
{
;//DebugBreak();
}
if (function.m_name == "RegisterClassA")
{
;//DebugBreak();
}
if (
(function.m_isBase && (std::binary_search(BEGIN(BaseFunctions), END(BaseFunctions), function.m_name)
|| std::binary_search(BEGIN(BaseFunctions), END(BaseFunctions), function.m_nameGeneric)))
|| (function.m_isUser && (std::binary_search(BEGIN(UserFunctions), END(UserFunctions), function.m_name)
|| std::binary_search(BEGIN(UserFunctions), END(UserFunctions), function.m_nameGeneric)))
|| function.m_isComctl
//true
)
{
function.FormParameterStrings();
m_functions.push_back(function);
}
else
{
// just ignore it
}
}
void HackInsteadOfTokenizeAndParse()
{
/*
This is not a proper C/C++ tokenizer.
It works well enough for our purposes with commctrl.h.
We are interested in function declarations that start
WINCOMMCTRLAPI, and comments precending them that contain @Function.
Declarations end with a semicolon.
We ignore preprocessor directives.
We ignore slash continuation.
*/
std::string comment;
std::string declaration;
std::string::const_iterator i;
std::string::const_iterator j;
std::string::const_iterator startOfDeclaration = m_string.end();
std::string::const_iterator endOfDeclaration;
std::string::const_iterator startOfComment;
std::string::const_iterator endOfComment;
std::string::const_iterator startOfLine = m_string.begin();
bool gotRparen = false;
bool gotLparen = false;
/* This might become useful.
std::vector<std::string> typeNames;
static const char* typenamesConstData[] =
{
"bool", "char", "short", "int", "long", "float", "double", "void",
"BOOL", "LRESULT", "HRESULT" "HIMAGELIST", "WORD", "DWORD", "ULONG"
"UINT", "INT", "BYTE", "COLORREF", "void *", "HDPA",
"HANDLE, "HWND", "POINT", "LPRECT", "LPINT", "LPSCROLLINFO",
"INT_PTR", "UINT_PTR", "LONG_PTR", "ULONG_PTR",
};
for (const char** pp = typenamesConstData ; pp != typenamesConstData + NUMBER_OF(typenamesConstData) ; ++pp)
{
typeNames.push_back(*pp);
}
*/
CFunction function;
for (i = m_string.begin() ; i != m_string.end() ; )
{
switch (*i)
{
Ldefault:
default:
startOfLine = m_string.end();
++i;
break;
case '#':
/*
Macros happen do not occur in the middle of declarations that we care about.
They do keep us away from declrations that might otherwise mess us up, lik
WinMain.
*/
startOfDeclaration = m_string.end();
goto Ldefault;
case ';':
if (startOfDeclaration == m_string.end()
|| !gotLparen
|| !gotRparen
)
goto Ldefault;
endOfDeclaration = i;
declaration.assign(startOfDeclaration, endOfDeclaration);
startOfDeclaration = m_string.end();
ProcessDeclaration(function, comment, declaration);
comment.erase();
declaration.erase();
function.Clear();
gotLparen = false;
gotRparen = false;
goto Ldefault;
case 'W':
if (startOfLine == m_string.end()
|| (i + 1) == m_string.end()
|| (i + 2) == m_string.end()
|| (i + 3) == m_string.end()
|| (i + 4) == m_string.end()
|| (i + 5) == m_string.end()
|| *(i + 1) != 'I'
|| *(i + 2) != 'N'
|| *(i + 3) == 'A'
|| *(i + 4) == 'P'
|| *(i + 5) == 'I'
)
goto Ldefault;
const char* s;
startOfDeclaration = i;
gotLparen = false;
gotRparen = false;
++i;
for (j = startOfDeclaration, s = "WINCOMMCTRLAPI"; j != m_string.end() && *s ; ++s, ++j)
{
if (*j != *s)
break;
}
if (*s == 0)
{
function.m_isComctl = true;
break;
}
for (j = startOfDeclaration, s = "WINUSERAPI"; j != m_string.end() && *s ; ++s, ++j)
{
if (*j != *s)
break;
}
if (*s == 0)
{
function.m_isUser = true;
break;
}
for (j = startOfDeclaration, s = "WINBASEAPI"; j != m_string.end() && *s ; ++s, ++j)
{
if (*j != *s)
break;
}
if (*s == 0)
{
function.m_isBase = true;
break;
}
startOfDeclaration = m_string.end();
break;
case '(':
gotLparen = (startOfDeclaration != m_string.end());
goto Ldefault;
case ')':
gotRparen = (gotLparen && startOfDeclaration != m_string.end());
goto Ldefault;
case '/':
if (startOfLine == m_string.end())
goto Ldefault;
++i;
if (*i == '*')
{
++i;
startOfComment = i;
comment.erase();
for (; i != m_string.end() && comment.empty(); ++i)
{
switch (*i)
{
default:
break;
case '*':
endOfComment = i;
i++;
/* allow for comments like this ***/
while (*i == '*')
{
endOfComment = i;
i++;
}
if (*i == '/')
{
++i;
comment.assign(startOfComment, endOfComment);
//printf("/*\n%s\n*/\n", comment.c_str());
}
break;
}
}
}
break;
case '\n':
case '\r':
for ( ; i != m_string.end() && (*i == '\n' || *i == '\r'); ++i )
{
/* nothing */
}
startOfLine = i;
break;
case ' ':
case '\t':
for ( ; i != m_string.end() && (*i == ' ' || *i == '\t'); ++i )
{
/* nothing */
}
// leave startOfLine unchanged
break;
}
}
}
void PrintStubs()
{
for (CFunctions::const_iterator i = m_functions.begin() ; i != m_functions.end() ; ++i)
{
i->PrintStub(stdout);
}
}
/*
-in foo.c -out x.h
*/
void System(const char* s)
{
printf("%s\n", s);
system(s);
}
std::string m_defines;
std::string m_includes;
void operator()(int argc, char** argv)
{
std::sort(BEGIN(BaseFunctions), END(BaseFunctions));
std::sort(BEGIN(UserFunctions), END(UserFunctions));
std::sort(BEGIN(AlsoNoFusionFunctions), END(AlsoNoFusionFunctions));
OpenAndRead("winbase.h");
StripComments(STRIPCOMMENTS_SLASHSLASH, m_string);
HackInsteadOfTokenizeAndParse();
OpenAndRead("winuser.h");
StripComments(STRIPCOMMENTS_SLASHSLASH, m_string);
HackInsteadOfTokenizeAndParse();
OpenAndRead("commctrl.h");
StripComments(STRIPCOMMENTS_SLASHSLASH, m_string);
HackInsteadOfTokenizeAndParse();
OpenAndRead("prsht.h");
StripComments(STRIPCOMMENTS_SLASHSLASH, m_string);
HackInsteadOfTokenizeAndParse();
printf("%s\n", prefix);
#if 0
while (*++argv)
{
std::string s = *argv;
FindAndReplaceChar(s, 'n', '\n');
FindAndReplaceChar(s, 'r', '\r');
StripComments(0, s);
printf("%s\n", s.c_str());
}
#endif
PrintStubs();
}
};
int main(int argc, char** argv)
{
CMakeSideBySideCommonControls makeSideBySideCommonControls;
makeSideBySideCommonControls(argc, argv);
return 0;
}