spiegel-keyman/developer/src/kmcmpdll/Compiler.cpp
Sabine fab2670c9e Merge branch 'refactor/developer/6026-kmcompx-compiler-cross-platform-part-2-NEW_VERSION' of https://github.com/keymanapp/keyman into refactor/developer/6026-kmcompx-compiler-cross-platform-part-2-NEW_VERSION
# Conflicts:
#	developer/src/kmcmpdll/CheckForDuplicates.cpp
#	developer/src/kmcmpdll/Compiler.cpp
#	developer/src/kmcmpdll/NamedCodeConstants.cpp
conflicts solved
2022-09-08 17:01:40 +02:00

3866 lines
111 KiB
C++

/*
Name: Compiler
Copyright: Copyright (C) SIL International.
Documentation:
Description:
Create Date: 20 Jun 2006
Modified Date: 25 Oct 2016
Authors: mcdurdin
Related Files:
Dependencies:
Bugs:
Todo:
Notes:
History: 20 Jun 2006 - mcdurdin - Initial version
23 Aug 2006 - mcdurdin - Add VISUALKEYBOARD, KMW_RTL, KMW_HELPFILE, KMW_HELPTEXT, KMW_EMBEDJS system stores
14 Sep 2006 - mcdurdin - Support icons in version 7
28 Sep 2006 - mcdurdin - Added product validation
28 Sep 2006 - mcdurdin - Added test for version 7.0 icon support
06 Oct 2006 - mcdurdin - Fix buffer overflow in UTF8 conversion
04 Dec 2006 - mcdurdin - Fix readfile buffer bug
04 Jan 2007 - mcdurdin - Add notany support
22 Jan 2007 - mcdurdin - Add K_NPENTER reference
25 Jan 2007 - mcdurdin - Resize buffers to 4095
30 May 2007 - mcdurdin - I786 - Compiler crash if zero length string in keystroke any
23 Aug 2007 - mcdurdin - I1011 - Fix buffer clobbering for UTF8 conversion of large files
27 Mar 2008 - mcdurdin - I1358 - Support for multiple languages for office config
14 Jun 2008 - mcdurdin - Support documenting language id as a single WORD instead of by PRIMARY/SUB
14 Jun 2008 - mcdurdin - Support Windows languages list
28 Jul 2008 - mcdurdin - I1569 - line prefixes
22 Mar 2010 - mcdurdin - Compiler fixup - x64 support
25 May 2010 - mcdurdin - I1632 - Keyboard Options
24 Jun 2010 - mcdurdin - I2432 - Use local buffers so GetXString can be re-entrant (used by if())
26 Jul 2010 - mcdurdin - I2467 - 8.0 renumber
18 Mar 2011 - mcdurdin - I2646 - Compiler warning on invalid Ethnologue codes
18 Mar 2011 - mcdurdin - I2525 - Unterminated string can crash compiler
19 Jul 2011 - mcdurdin - I2993 - Named code constants cause a warning 0x208D to appear
26 Jun 2012 - mcdurdin - I3377 - KM9 - Update code references from 8.0 to 9.0
17 Aug 2012 - mcdurdin - I3431 - V9.0 - Spaces in values in if comparisons break the compiler parser
27 Aug 2012 - mcdurdin - I3439 - V9.0 - Refactor xstring support in C++ code
27 Aug 2012 - mcdurdin - I3437 - V9.0 - Add support for set(&layer) and layer()
27 Aug 2012 - mcdurdin - I3438 - V9.0 - Add support for custom virtual keys
27 Aug 2012 - mcdurdin - I3430 - V9.0 - Add support for if(&platform) and if(&baselayout) to compilers
27 Aug 2012 - mcdurdin - I3440 - V9.0 - Tidy up set statement delimiter recognition
24 Oct 2012 - mcdurdin - I3483 - V9.0 - Add support for compiling in the layout file
24 Oct 2012 - mcdurdin - I3481 - V9.0 - Eliminate unsafe calls in C++
24 Jan 2012 - mcdurdin - I3137 - If key part of VK rule is missing, compiler generates invalid file
06 Feb 2012 - mcdurdin - I3228 - kmcmpdll sometimes tries to write temp files to Program Files
03 Nov 2012 - mcdurdin - I3510 - V9.0 - Merge of I3228 - kmcmpdll sometimes tries to write temp files to Program Files
03 Nov 2012 - mcdurdin - I3511 - V9.0 - Merge of I3137 - If key part of VK rule is missing, compiler generates invalid file
13 Dec 2012 - mcdurdin - I3654 - V9.0 - Compiler appears to create unregistered keyboards even when registered
13 Dec 2012 - mcdurdin - I3641 - V9.0 - compiler dll buffer overrun bugs
13 Dec 2012 - mcdurdin - I3681 - V9.0 - KeymanWeb compiler should output formatted js when debug=1
13 Dec 2012 - mcdurdin - I3686 - V9.0 - AddStore attaches property flags to wrong store structure
19 Mar 2014 - mcdurdin - I4140 - V9.0 - Add keyboard version information to keyboards
04 Nov 2014 - mcdurdin - I4504 - V9.0 - Consolidate the compile action into single command
02 Jul 2015 - mcdurdin - I4784 - Compiler does not recognise baselayout, layer or platform at the start of a line
02 Jul 2015 - mcdurdin - I4785 - baselayout(), layer() and platform() produce incorrect compiled code
24 Aug 2015 - mcdurdin - I4865 - Add treat hints and warnings as errors into project
24 Aug 2015 - mcdurdin - I4866 - Add warn on deprecated features to project and compile
24 Aug 2015 - mcdurdin - I4867 - Add test for code after use warning to compile
06 Nov 2015 - mcdurdin - I4914 - kmcmpdll does not pick an index() statement that has an offset one past the key
23 Feb 2016 - mcdurdin - I4982 - Defined character constants cannot be referenced correctly in other stores
25 Oct 2016 - mcdurdin - I5135 - Remove product and licensing references from Developer projects
*/
#include <pch.h>
#include <compfile.h>
#include <comperr.h>
#include <../../../common/windows/cpp/include/vkeys.h>
#include <cuchar>
#include <versioning.h>
#include <kmcmpdll.h>
#include <DeprecationChecks.h>
#include <virtualcharkeys.h>
#include <../../../common/windows/cpp/include/crc32.h>
#include <../../../common/windows/cpp/include/ConvertUTF.h>
#include <debugstore.h>
#include <namedcodeconstants.h>
#include <../../../common/windows/cpp/include/unicode.h>
#include <edition.h>
#include <CharToKeyConversion.h>
#include <CasedKeys.h>
#include <vector>
#include <../../src/kmcmpdll/xstring.h>
#include <CheckNCapsConsistency.h>
#include <CheckFilenameConsistency.h>
#include <UnreachableRules.h>
#include <CheckForDuplicates.h>
#include <kmx_u16.h>
int xatoi(PKMX_WCHAR *p);
int atoiW(PKMX_WCHAR p);
void safe_wcsncpy(PKMX_WCHAR out, PKMX_WCHAR in, int cbMax);
int UTF32ToUTF16(int n, int *n1, int *n2);
int GetDeadKey(PFILE_KEYBOARD fk, PKMX_WCHAR p);
KMX_BOOL IsValidCallStore(PFILE_STORE fs);
KMX_BOOL IsSameToken(PKMX_WCHAR *p, KMX_WCHAR const * token);
KMX_DWORD GetRHS(PFILE_KEYBOARD fk, PKMX_WCHAR p, PKMX_WCHAR buf, int bufsize, int offset, int IsUnicode);
PKMX_WCHAR GetDelimitedString(PKMX_WCHAR *p, KMX_WCHAR const * Delimiters, KMX_WORD Flags);
KMX_DWORD GetXString(PFILE_KEYBOARD fk, PKMX_WCHAR str, KMX_WCHAR const * token, PKMX_WCHAR output, int max, int offset, PKMX_WCHAR *newp, int isVKey, int isUnicode);
int GetGroupNum(PFILE_KEYBOARD fk, PKMX_WCHAR p);
int LineTokenType(PKMX_WCHAR *str);
KMX_DWORD ParseLine(PFILE_KEYBOARD fk, PKMX_WCHAR str);
KMX_DWORD ProcessGroupFinish(PFILE_KEYBOARD fk);
KMX_DWORD ProcessGroupLine(PFILE_KEYBOARD fk, PKMX_WCHAR p);
KMX_DWORD ProcessStoreLine(PFILE_KEYBOARD fk, PKMX_WCHAR p);
KMX_DWORD AddDebugStore(PFILE_KEYBOARD fk, KMX_WCHAR const * str);
KMX_DWORD ProcessKeyLine(PFILE_KEYBOARD fk, PKMX_WCHAR str, KMX_BOOL IsUnicode);
KMX_DWORD ProcessEthnologueStore(PKMX_WCHAR p); // I2646
KMX_DWORD ProcessHotKey(PKMX_WCHAR p, KMX_DWORD *hk);
KMX_DWORD ImportBitmapFile(PFILE_KEYBOARD fk, PKMX_WCHAR szName, PKMX_DWORD FileSize, PKMX_BYTE *Buf);
KMX_DWORD ExpandKp(PFILE_KEYBOARD fk, PFILE_KEY kpp, KMX_DWORD storeIndex);
KMX_DWORD ReadLine(FILE* fp_in , PKMX_WCHAR wstr, KMX_BOOL PreProcess);
KMX_DWORD WriteCompiledKeyboard(PFILE_KEYBOARD fk, FILE* fp_out);
KMX_BOOL CompileKeyboardHandle(FILE* fp_in, PFILE_KEYBOARD fk);
int GetVKCode(PFILE_KEYBOARD fk, PKMX_WCHAR p); // I3438 // TODO: Consolidate GetDeadKey and GetVKCode?
KMX_DWORD BuildVKDictionary(PFILE_KEYBOARD fk); // I3438
KMX_DWORD AddStore(PFILE_KEYBOARD fk, KMX_DWORD SystemID, KMX_WCHAR const * str, KMX_DWORD *dwStoreID= NULL);
KMX_DWORD ProcessSystemStore(PFILE_KEYBOARD fk, KMX_DWORD SystemID, PFILE_STORE sp);
void RecordDeadkeyNames(PFILE_KEYBOARD fk);
KMX_DWORD AddCompilerVersionStore(PFILE_KEYBOARD fk);
KMX_BOOL CheckStoreUsage(PFILE_KEYBOARD fk, int storeIndex, KMX_BOOL fIsStore, KMX_BOOL fIsOption, KMX_BOOL fIsCall);
KMX_DWORD process_if(PFILE_KEYBOARD fk, LPKMX_WCHAR q, LPKMX_WCHAR tstr, int *mx);
KMX_DWORD process_reset(PFILE_KEYBOARD fk, LPKMX_WCHAR q, LPKMX_WCHAR tstr, int *mx);
KMX_DWORD process_set(PFILE_KEYBOARD fk, LPKMX_WCHAR q, LPKMX_WCHAR tstr, int *mx);
KMX_DWORD process_save(PFILE_KEYBOARD fk, LPKMX_WCHAR q, LPKMX_WCHAR tstr, int *mx);
KMX_DWORD process_platform(PFILE_KEYBOARD fk, LPKMX_WCHAR q, LPKMX_WCHAR tstr, int *mx); // I3430
KMX_DWORD process_baselayout(PFILE_KEYBOARD fk, LPKMX_WCHAR q, LPKMX_WCHAR tstr, int *mx); // I3430
KMX_DWORD process_set_synonym(KMX_DWORD dwSystemID, PFILE_KEYBOARD fk, LPKMX_WCHAR q, LPKMX_WCHAR tstr, int *mx); // I3437
KMX_DWORD process_expansion(PFILE_KEYBOARD fk, LPKMX_WCHAR q, LPKMX_WCHAR tstr, int *mx, int max);
KMX_BOOL IsValidKeyboardVersion(KMX_WCHAR *dpString);
FILE* UTF16TempFromUTF8(FILE* fp_in , KMX_BOOL hasPreamble);
const KMX_WCHAR * LineTokens[] = {
u"SVNBHBGMNSCCLLCMLB", u"store", u"VERSION ", u"NAME ",
u"BITMAP ", u"HOTKEY ", u"begin", u"group", u"match", u"nomatch",
u"SHIFT FREES CAPS", u"CAPS ON ONLY", u"CAPS ALWAYS OFF",
u"LANGUAGE ", u"LAYOUT ", u"COPYRIGHT ", u"MESSAGE ", u"LANGUAGENAME ",
u"BITMAPS " };
#define SSN__PREFIX u"&"
const KMX_WCHAR * StoreTokens[TSS__MAX + 2] = {
u"",
SSN__PREFIX u"BITMAP",
SSN__PREFIX u"COPYRIGHT",
SSN__PREFIX u"HOTKEY",
SSN__PREFIX u"LANGUAGE",
SSN__PREFIX u"LAYOUT",
SSN__PREFIX u"MESSAGE",
SSN__PREFIX u"NAME",
SSN__PREFIX u"VERSION",
SSN__PREFIX u"CAPSONONLY",
SSN__PREFIX u"CAPSALWAYSOFF",
SSN__PREFIX u"SHIFTFREESCAPS",
SSN__PREFIX u"LANGUAGENAME",
u"",
u"",
SSN__PREFIX u"ETHNOLOGUECODE",
u"",
SSN__PREFIX u"MNEMONICLAYOUT",
SSN__PREFIX u"INCLUDECODES",
SSN__PREFIX u"OLDCHARPOSMATCHING",
u"",
u"",
u"",
u"",
SSN__PREFIX u"VISUALKEYBOARD",
SSN__PREFIX u"KMW_RTL",
SSN__PREFIX u"KMW_HELPFILE",
SSN__PREFIX u"KMW_HELPTEXT",
SSN__PREFIX u"KMW_EMBEDJS",
SSN__PREFIX u"WINDOWSLANGUAGES",
u"",
SSN__PREFIX u"PLATFORM", // read only // I3430
SSN__PREFIX u"BASELAYOUT", // read only // I3430
SSN__PREFIX u"LAYER", // read-write via set? // I3430
u"", // I3438
SSN__PREFIX u"LAYOUTFILE", // I3483
SSN__PREFIX u"KEYBOARDVERSION", // I4140
SSN__PREFIX u"KMW_EMBEDCSS",
SSN__PREFIX u"TARGETS", // I4504
SSN__PREFIX u"CASEDKEYS", // #2241
SSN__PREFIX u"", // TSS_BEGIN_NEWCONTEXT
SSN__PREFIX u"", // TSS_BEGIN_POSTKEYSTROKE
SSN__PREFIX u"NEWLAYER",
SSN__PREFIX u"OLDLAYER",
NULL
};
static_assert(_countof(StoreTokens) == TSS__MAX + 2, "StoreTokens should have exactly TSS__MAX+2 elements");
enum LinePrefixType { lptNone, lptKeymanAndKeymanWeb, lptKeymanWebOnly, lptKeymanOnly, lptOther };
/* Compile target */
HINSTANCE g_hInstance;
CompilerMessageProc msgproc = NULL;
int currentLine = 0, nErrors = 0;
KMX_CHAR CompileDir[MAX_PATH];
int ErrChr;
KMX_CHAR ErrExtra[256];
KMX_BOOL FSaveDebug, FCompilerWarningsAsErrors, FWarnDeprecatedCode; // I4865 // I4866
KMX_BOOL FShouldAddCompilerVersion = TRUE;
KMX_BOOL FOldCharPosMatching = FALSE, FMnemonicLayout = FALSE;
NamedCodeConstants *CodeConstants = NULL;
/* Compile target */
int CompileTarget;
#define CKF_KEYMAN 0
#define CKF_KEYMANWEB 1
KMX_BOOL WINAPI DllMain(HINSTANCE hinst, KMX_DWORD fdwReason, LPVOID lpvReserved)
{
if (fdwReason == DLL_PROCESS_ATTACH) g_hInstance = hinst;
return TRUE;
}
PKMX_WCHAR strtowstr(PKMX_STR in)
{
PKMX_WCHAR result;
auto c = u16string_from_string(in);
result = new KMX_WCHAR[c.length() + 1];
u16cpy(result, c.c_str());
return result;
}
PKMX_STR wstrtostr(PKMX_WCHAR in)
{
PKMX_STR result;
auto c = string_from_u16string(in);
result = new KMX_CHAR[c.length() + 1];
strcpy(result, c.c_str());
return result;
}
KMX_BOOL AddCompileString(LPSTR buf)
{
SetLastError(0);
(*msgproc)(currentLine + 1, CWARN_Info, buf);
return FALSE;
}
KMX_BOOL AddCompileMessage(KMX_DWORD msg)
{
KMX_CHAR szText[SZMAX_ERRORTEXT + 1 + 280];
SetLastError(0);
if (msg & CERR_FATAL)
{
LoadString(g_hInstance, msg, szText, SZMAX_ERRORTEXT);
(*msgproc)(currentLine + 1, msg, szText);
nErrors++;
return TRUE;
}
if (msg & CERR_ERROR) nErrors++;
LoadString(g_hInstance, msg, szText, SZMAX_ERRORTEXT);
if (ErrChr > 0)
sprintf(strchr(szText, 0), " character offset:%d", ErrChr);
if (*ErrExtra)
sprintf(strchr(szText, 0), " extra:%s", ErrExtra);
ErrChr = 0; *ErrExtra = 0;
if (!(*msgproc)(currentLine, msg, szText)) return TRUE;
return FALSE;
}
typedef struct _COMPILER_OPTIONS {
KMX_DWORD dwSize;
bool ShouldAddCompilerVersion;
} COMPILER_OPTIONS;
typedef COMPILER_OPTIONS *PCOMPILER_OPTIONS;
extern "C" KMX_BOOL __declspec(dllexport) SetCompilerOptions(PCOMPILER_OPTIONS options) {
if(!options || options->dwSize < sizeof(COMPILER_OPTIONS)) {
return FALSE;
}
FShouldAddCompilerVersion = options->ShouldAddCompilerVersion;
return TRUE;
}
extern "C" KMX_BOOL __declspec(dllexport) CompileKeyboardFile(PKMX_STR pszInfile, PKMX_STR pszOutfile, KMX_BOOL ASaveDebug, KMX_BOOL ACompilerWarningsAsErrors, KMX_BOOL AWarnDeprecatedCode, CompilerMessageProc pMsgProc) // I4865 // I4866
{
FILE* fp_in = NULL;
FILE* fp_out = NULL;
KMX_BOOL err;
KMX_DWORD len;
KMX_CHAR str[260];
FSaveDebug = ASaveDebug;
FCompilerWarningsAsErrors = ACompilerWarningsAsErrors; // I4865
FWarnDeprecatedCode = AWarnDeprecatedCode; // I4866
CompileTarget = CKF_KEYMAN;
if (!pMsgProc || !pszInfile || !pszOutfile) SetError(CERR_BadCallParams);
PKMX_STR p;
if (p = strrchr(pszInfile, '\\'))
{
strncpy_s(CompileDir, _countof(CompileDir), pszInfile, (INT_PTR)(p - pszInfile + 1)); // I3481
CompileDir[(INT_PTR)(p - pszInfile + 1)] = 0;
}
else
CompileDir[0] = 0;
msgproc = pMsgProc;
currentLine = 0;
nErrors = 0;
fp_in = fopen((const char*)pszInfile,"rb");
if (fp_in == NULL) SetError(CERR_InfileNotExist);
// Transfer the file to a memory stream for processing UTF-8 or ANSI to UTF-16?
// What about really large files? Transfer to a temp file...
fseek(fp_in, 0, SEEK_END);
len = ftell(fp_in);
fseek(fp_in, 0, SEEK_SET);
if( !fread(str,1,3,fp_in))
{
fclose(fp_in);
return CERR_CannotReadInfile;
}
fseek( fp_in,0,SEEK_SET);
if (str[0] == UTF8Sig[0] && str[1] == UTF8Sig[1] && str[2] == UTF8Sig[2])
fp_in = UTF16TempFromUTF8(fp_in, TRUE);
else if (str[0] == UTF16Sig[0] && str[1] == UTF16Sig[1])
fseek( fp_in,2,SEEK_SET);
else
fp_in = UTF16TempFromUTF8(fp_in, FALSE);
if (fp_in == NULL)
{
return CERR_CannotCreateTempfile;
}
fp_out = fopen((const char*)pszOutfile,"wb");
if (fp_out == NULL) SetError(CERR_CannotCreateOutfile);
KMX_DWORD msg;
FILE_KEYBOARD fk;
CodeConstants = new NamedCodeConstants;
err = CompileKeyboardHandle(fp_in, &fk);
if (err)
{
if ((msg = WriteCompiledKeyboard(&fk, fp_out)) != CERR_None)
AddCompileMessage(msg);
}
else
AddCompileMessage(CERR_InvalidValue);
fclose(fp_in);
fclose(fp_out);
delete CodeConstants;
if (nErrors > 0)
{
remove(pszOutfile);
return FALSE;
}
return err;
}
extern "C" KMX_BOOL __declspec(dllexport) CompileKeyboardFileToBuffer(PKMX_STR pszInfile, PFILE_KEYBOARD pfkBuffer, KMX_BOOL ACompilerWarningsAsErrors, KMX_BOOL AWarnDeprecatedCode, CompilerMessageProc pMsgProc, int Target) // I4865 // I4866
{
FILE* fp_in =NULL;
KMX_BOOL err;
KMX_DWORD len;
KMX_DWORD len2;
KMX_CHAR str[260];
FSaveDebug = TRUE; // I3681
FCompilerWarningsAsErrors = ACompilerWarningsAsErrors; // I4865
FWarnDeprecatedCode = AWarnDeprecatedCode; // I4866
CompileTarget = Target;
if (!pMsgProc || !pszInfile || !pfkBuffer) SetError(CERR_BadCallParams);
PKMX_STR p;
if (p = strrchr(pszInfile, '\\'))
{
strncpy_s(CompileDir, _countof(CompileDir), pszInfile, (INT_PTR)(p - pszInfile + 1)); // I3481
CompileDir[(INT_PTR)(p - pszInfile + 1)] = 0;
}
else
CompileDir[0] = 0;
msgproc = pMsgProc;
currentLine = 0;
nErrors = 0;
fp_in = fopen(pszInfile,"rb");
if (fp_in == NULL) SetError(CERR_InfileNotExist);
// Transfer the file to a memory stream for processing UTF-8 or ANSI to UTF-16?
// What about really large files? Transfer to a temp file...
KMX_DWORD sz = ftell(fp_in);
len2 = fread(str,1,sz,fp_in);
if( !len2)
{
fclose(fp_in);
return CERR_CannotReadInfile;
}
fseek( fp_in,0,SEEK_SET);
if (str[0] == UTF8Sig[0] && str[1] == UTF8Sig[1] && str[2] == UTF8Sig[2])
fp_in = UTF16TempFromUTF8(fp_in, TRUE);
else if (str[0] == UTF16Sig[0] && str[1] == UTF16Sig[1])
fseek( fp_in,2,SEEK_SET);
else
fp_in = UTF16TempFromUTF8(fp_in, FALSE);
CodeConstants = new NamedCodeConstants;
err = CompileKeyboardHandle(fp_in, pfkBuffer);
delete CodeConstants;
fclose(fp_in);
if (nErrors > 0)
return FALSE;
return err;
}
void GetVersionInfo(KMX_DWORD *VersionMajor, KMX_DWORD *VersionMinor)
{
HRSRC hres = FindResource(0, MAKEINTRESOURCE(1), RT_VERSION);
if (hres)
{
HGLOBAL hmem = LoadResource(0, hres);
PKMX_STR buf = (PKMX_STR)LockResource(hmem);
*VersionMajor = *((PKMX_DWORD)&buf[0x30]);
*VersionMinor = *((PKMX_DWORD)&buf[0x34]);
}
}
KMX_BOOL CompileKeyboardHandle(FILE* fp_in, PFILE_KEYBOARD fk)
{
PKMX_WCHAR str, p;
KMX_DWORD msg;
FMnemonicLayout = FALSE;
if (!fk) {
SetError(CERR_SomewhereIGotItWrong);
}
str = new KMX_WCHAR[LINESIZE];
if (!str) {
SetError(CERR_CannotAllocateMemory);
}
fk->KeyboardID = 0;
fk->version = 0;
fk->dpStoreArray = NULL;
fk->dpGroupArray = NULL;
fk->cxStoreArray = 0;
fk->cxGroupArray = 0;
fk->StartGroup[0] = fk->StartGroup[1] = -1;
fk->szName[0] = 0;
fk->szCopyright[0] = 0;
fk->dwFlags = KF_AUTOMATICVERSION;
fk->currentGroup = 0xFFFFFFFF;
fk->currentStore = 0;
fk->cxDeadKeyArray = 0;
fk->dpDeadKeyArray = NULL;
fk->cxVKDictionary = 0; // I3438
fk->dpVKDictionary = NULL; // I3438
/* fk->szMessage[0] = 0;
fk->szLanguageName[0] = 0;*/
fk->dwBitmapSize = 0;
fk->dwHotKey = 0;
/* Add a store for the Keyman 6.0 copyright information string */
if(FShouldAddCompilerVersion) {
KMX_DWORD vmajor, vminor;
GetVersionInfo(&vmajor, &vminor);
u16sprintf(str,LINESIZE, L"Created with Keyman Developer version %d.%d.%d.%d", HIWORD(vmajor), LOWORD(vmajor), HIWORD(vminor), LOWORD(vminor));
AddStore(fk, TSS_KEYMANCOPYRIGHT, str);
}
/* Add a system store for the Keyman edition number */
u16sprintf(str, LINESIZE, L"%d", 0); // I3481
AddStore(fk, TSS_CUSTOMKEYMANEDITION, str);
PKMX_WCHAR tbuf = strtowstr((KMX_CHAR*) "Keyman");
AddStore(fk, TSS_CUSTOMKEYMANEDITIONNAME, tbuf);
delete tbuf;
// must preprocess for group and store names -> this isn't really necessary, but never mind!
while ((msg = ReadLine(fp_in, str, TRUE)) == CERR_None)
{
if (GetAsyncKeyState(VK_ESCAPE) < 0) SetError(CERR_Break);
p = str;
switch (LineTokenType(&p))
{
case T_VERSION:
*(p + 4) = 0;
if ((msg = AddStore(fk, TSS_VERSION, p)) != CERR_None) SetError(msg);
break;
case T_GROUP:
if ((msg = ProcessGroupLine(fk, p)) != CERR_None) SetError(msg);
break;
case T_STORE:
if ((msg = ProcessStoreLine(fk, p)) != CERR_None) SetError(msg);
break;
default:
break;
}
}
if (msg != CERR_EndOfFile) SetError(msg);
fseek( fp_in,2,SEEK_SET);
currentLine = 0;
/* Reindex the list of codeconstants after stores added */
CodeConstants->reindex();
/* ReadLine will automatically skip over $Keyman lines, and parse wrapped lines */
while ((msg = ReadLine(fp_in, str, FALSE)) == CERR_None)
{
if (GetAsyncKeyState(VK_ESCAPE) < 0) SetError(CERR_Break);
msg = ParseLine(fk, str);
if (msg != CERR_None) SetError(msg);
}
if (msg != CERR_EndOfFile) SetError(msg);
ProcessGroupFinish(fk);
if (FSaveDebug) RecordDeadkeyNames(fk);
/* Add the compiler version as a system store */
if ((msg = AddCompilerVersionStore(fk)) != CERR_None) SetError(msg);
if ((msg = BuildVKDictionary(fk)) != CERR_None) SetError(msg); // I3438
if ((msg = CheckFilenameConsistencyForCalls(fk)) != CERR_None) SetError(msg);
delete str;
if (!CheckKeyboardFinalVersion(fk)) {
return FALSE;
}
/* Warn on inconsistent use of NCAPS */
if (!FMnemonicLayout) {
CheckNCapsConsistency(fk);
}
/* Flag presence of deprecated features */
CheckForDeprecatedFeatures(fk);
return TRUE;
}
KMX_DWORD ProcessBeginLine(PFILE_KEYBOARD fk, PKMX_WCHAR p)
{
KMX_WCHAR tstr[128];
PKMX_WCHAR q, pp;
int BeginMode;
KMX_DWORD msg;
pp = p;
q = ( PKMX_WCHAR) u16chr(p, '>');
if (!q) return CERR_NoTokensFound;
while (iswspace(*p)) p++;
if (u16nicmp(p, u"unicode", 7) == 0) BeginMode = BEGIN_UNICODE;
else if (u16nicmp(p, u"ansi", 4) == 0) BeginMode = BEGIN_ANSI;
else if (u16nicmp(p, u"newContext", 10) == 0) BeginMode = BEGIN_NEWCONTEXT;
else if (u16nicmp(p, u"postKeystroke", 13) == 0) BeginMode = BEGIN_POSTKEYSTROKE;
else if (*p != '>') return CERR_InvalidToken;
else BeginMode = BEGIN_ANSI;
if ((msg = GetRHS(fk, p, tstr, 80, (int)(INT_PTR)(p - pp), FALSE)) != CERR_None) return msg;
if (tstr[0] != UC_SENTINEL || tstr[1] != CODE_USE) {
return CERR_InvalidBegin;
}
if (tstr[3] != 0) {
return CERR_InvalidToken;
}
if (BeginMode == BEGIN_ANSI || BeginMode == BEGIN_UNICODE) {
fk->StartGroup[BeginMode] = tstr[2] - 1;
//mcd-03-01-2000: removed the secondary group idea; this was undocumented and
//is not supported under Keyman 5.0: ugly!!
//if(tstr[3] == UC_SENTINEL && tstr[4] == CODE_USE) fk->StartGroup[1] = tstr[5] - 1;
if (FSaveDebug) {
/* Record a system store for the line number of the begin statement */
AddDebugStore(fk, BeginMode == BEGIN_UNICODE ? DEBUGSTORE_BEGIN u"Unicode" : DEBUGSTORE_BEGIN u"ANSI");
}
}
else {
PFILE_GROUP gp = &fk->dpGroupArray[tstr[2] - 1];
if (!gp->fReadOnly) {
return BeginMode == BEGIN_NEWCONTEXT ?
CERR_NewContextGroupMustBeReadonly :
CERR_PostKeystrokeGroupMustBeReadonly;
}
return AddStore(fk, BeginMode == BEGIN_NEWCONTEXT ? TSS_BEGIN_NEWCONTEXT : TSS_BEGIN_POSTKEYSTROKE, tstr, NULL);
}
return CERR_None;
}
KMX_DWORD ValidateMatchNomatchOutput(PKMX_WCHAR p) {
while (p && *p) {
if (*p == UC_SENTINEL) {
switch (*(p + 1)) {
case CODE_CONTEXT:
case CODE_CONTEXTEX:
case CODE_INDEX:
return CERR_ContextAndIndexInvalidInMatchNomatch;
}
}
p = incxstr(p);
}
return CERR_None;
}
KMX_DWORD ParseLine(PFILE_KEYBOARD fk, PKMX_WCHAR str)
{
PKMX_WCHAR p, q, pp;
PFILE_GROUP gp;
KMX_DWORD msg;
int IsUnicode = TRUE; // For NOW!
KMX_WCHAR sep[2] = u"\n";
PKMX_WCHAR p_sep =sep;
p = str;
pp = str;
switch (LineTokenType(&p))
{
case T_BLANK:
case T_COMMENT:
break; // Ignore the line
case T_VERSION:
case T_STORE:
break; // The line has already been processed
case T_BEGIN:
// after a begin can be "Unicode" or "ANSI" or nothing (=ANSI)
if ((msg = ProcessBeginLine(fk, p)) != CERR_None) return msg;
break;
case T_GROUP:
if (fk->currentGroup == 0xFFFFFFFF) fk->currentGroup = 0;
else
{
if ((msg = ProcessGroupFinish(fk)) != CERR_None) return msg; // finish off previous group first?
fk->currentGroup++;
}
// if( (err = ProcessGroupLine( fk, p )) != CERR_None ) return err;
break;
case T_NAME:
WarnDeprecatedHeader(); // I4866
q = GetDelimitedString(&p, u"\"\"", 0);
if (!q) return CERR_InvalidName;
if ((msg = AddStore(fk, TSS_NAME, q)) != CERR_None) return msg;
break;
case T_COPYRIGHT:
WarnDeprecatedHeader(); // I4866
q = GetDelimitedString(&p, u"\"\"", 0);
if (!q) return CERR_InvalidCopyright;
if ((msg = AddStore(fk, TSS_COPYRIGHT, q)) != CERR_None) return msg;
break;
case T_MESSAGE:
WarnDeprecatedHeader(); // I4866
q = GetDelimitedString(&p, u"\"\"", 0);
if (!q) return CERR_InvalidMessage;
if ((msg = AddStore(fk, TSS_MESSAGE, q)) != CERR_None) return msg;
break;
case T_LANGUAGENAME:
WarnDeprecatedHeader(); // I4866
q = GetDelimitedString(&p, u"\"\"", 0);
if (!q) return CERR_InvalidLanguageName;
if ((msg = AddStore(fk, TSS_LANGUAGENAME, q)) != CERR_None) return msg;
break;
case T_LANGUAGE:
{
WarnDeprecatedHeader(); // I4866
char16_t *tokcontext = NULL;
q = u16tok(p, p_sep, &tokcontext); // I3481
if ((msg = AddStore(fk, TSS_LANGUAGE, q)) != CERR_None) return msg;
break;
}
case T_LAYOUT:
{
WarnDeprecatedHeader(); // I4866
char16_t *tokcontext = NULL;
q = u16tok(p, p_sep, &tokcontext); // I3481
if ((msg = AddStore(fk, TSS_LAYOUT, q)) != CERR_None) return msg;
break;
}
case T_CAPSOFF:
WarnDeprecatedHeader(); // I4866
if ((msg = AddStore(fk, TSS_CAPSALWAYSOFF, u"1")) != CERR_None) return msg;
break;
case T_CAPSON:
WarnDeprecatedHeader(); // I4866
if ((msg = AddStore(fk, TSS_CAPSONONLY, u"1")) != CERR_None) return msg;
break;
case T_SHIFT:
WarnDeprecatedHeader(); // I4866
if ((msg = AddStore(fk, TSS_SHIFTFREESCAPS, u"1")) != CERR_None) return msg;
break;
case T_HOTKEY:
{
WarnDeprecatedHeader(); // I4866
char16_t *tokcontext = NULL;
if ((q = u16tok(p, p_sep, &tokcontext)) == NULL) return CERR_CodeInvalidInThisSection; // I3481
if ((msg = AddStore(fk, TSS_HOTKEY, q)) != CERR_None) return msg;
break;
}
case T_BITMAP:
{
WarnDeprecatedHeader(); // I4866
char16_t *tokcontext = NULL;
if ((q = u16tok(p, p_sep, &tokcontext)) == NULL) return CERR_InvalidBitmapLine; // I3481
while (iswspace(*q)) q++;
if (*q == '"') {
p = q;
q = GetDelimitedString(&p, u"\"\"", 0);
if (!q) return CERR_InvalidBitmapLine;
}
if ((msg = AddStore(fk, TSS_BITMAP, q)) != CERR_None) return msg;
break;
}
case T_BITMAPS:
{
WarnDeprecatedHeader(); // I4866
char16_t *tokcontext = NULL;
AddWarning(CWARN_BitmapNotUsed);
if ((q = u16tok(p, p_sep, &tokcontext)) == NULL) return CERR_InvalidBitmapLine; // I3481
if ((PKMX_WCHAR) u16chr(q, ',')) *(PKMX_WCHAR) u16chr(q, ',') = 0;
if ((msg = AddStore(fk, TSS_BITMAP, q)) != CERR_None) return msg;
break;
}
case T_KEYTOKEY: // A rule
if (fk->currentGroup == 0xFFFFFFFF) return CERR_CodeInvalidInThisSection;
if ((msg = ProcessKeyLine(fk, p, IsUnicode)) != CERR_None) return msg;
break;
case T_MATCH:
if (fk->currentGroup == 0xFFFFFFFF) return CERR_CodeInvalidInThisSection;
{
PKMX_WCHAR buf = new KMX_WCHAR[GLOBAL_BUFSIZE];
if ((msg = GetRHS(fk, p, buf, GLOBAL_BUFSIZE - 1, (int)(INT_PTR)(p - pp), IsUnicode)) != CERR_None)
{
delete buf;
return msg;
}
if ((msg = ValidateMatchNomatchOutput(buf)) != CERR_None) {
delete buf;
return msg;
}
gp = &fk->dpGroupArray[fk->currentGroup];
gp->dpMatch = new KMX_WCHAR[u16len(buf) + 1];
u16ncpy(gp->dpMatch, buf, u16len(buf) + 1); // I3481
delete buf;
if (FSaveDebug)
{
KMX_WCHAR tstr[128];
//swprintf(tstr, "%d", fk->currentGroup);
/* Record a system store for the line number of the begin statement */
//wcscpy(tstr, DEBUGSTORE_MATCH);
u16sprintf(tstr, _countof(tstr), L"%ls%d %ls",u16fmt( DEBUGSTORE_MATCH).c_str(), (int) fk->currentGroup, u16fmt(gp->szName).c_str()); // I3481
AddDebugStore(fk, tstr);
}
}
break;
case T_NOMATCH:
if (fk->currentGroup == 0xFFFFFFFF) return CERR_CodeInvalidInThisSection;
{
PKMX_WCHAR buf = new KMX_WCHAR[GLOBAL_BUFSIZE];
if ((msg = GetRHS(fk, p, buf, GLOBAL_BUFSIZE, (int)(INT_PTR)(p - pp), IsUnicode)) != CERR_None)
{
delete[] buf;
return msg;
}
if ((msg = ValidateMatchNomatchOutput(buf)) != CERR_None) {
delete[] buf;
return msg;
}
gp = &fk->dpGroupArray[fk->currentGroup];
gp->dpNoMatch = new KMX_WCHAR[u16len(buf) + 1];
u16ncpy(gp->dpNoMatch, buf, u16len(buf) + 1); // I3481
delete[] buf;
if (FSaveDebug)
{
KMX_WCHAR tstr[128];
PKMX_WCHAR p_tstr;
/* Record a system store for the line number of the begin statement */
u16sprintf(tstr, _countof(tstr), L"%ls%d %ls", u16fmt(DEBUGSTORE_NOMATCH).c_str(), fk->currentGroup, u16fmt(gp->szName).c_str()); // I3481
AddDebugStore(fk, tstr);
}
}
break;
default:
return CERR_InvalidToken;
}
return CERR_None;
}
//**********************************************************************************************************************
KMX_DWORD ProcessGroupLine(PFILE_KEYBOARD fk, PKMX_WCHAR p)
{
PFILE_GROUP gp;
PKMX_WCHAR q;
gp = new FILE_GROUP[fk->cxGroupArray + 1];
if (!gp) return CERR_CannotAllocateMemory;
if (fk->dpGroupArray)
{
memcpy(gp, fk->dpGroupArray, sizeof(FILE_GROUP) * fk->cxGroupArray);
delete fk->dpGroupArray;
}
fk->dpGroupArray = gp;
gp = &fk->dpGroupArray[fk->cxGroupArray];
fk->cxGroupArray++;
gp->dpKeyArray = NULL;
gp->dpMatch = NULL;
gp->dpNoMatch = NULL;
gp->cxKeyArray = 0;
q = GetDelimitedString(&p, u"()", GDS_CUTLEAD | GDS_CUTFOLL);
if (!q) return CERR_InvalidGroupLine;
gp->fUsingKeys = FALSE;
gp->fReadOnly = IsSameToken(&p, u"readonly");
if (!gp->fReadOnly) {
if (IsSameToken(&p, u"using") && IsSameToken(&p, u"keys"))
gp->fUsingKeys = TRUE;
}
safe_wcsncpy(gp->szName, q, SZMAX_GROUPNAME);
gp->Line = currentLine;
if (FSaveDebug)
{
KMX_WCHAR tstr[128];
PKMX_WCHAR p_tstr;
/* Record a system store for the line number of the begin statement */
u16sprintf(tstr, _countof(tstr), L"%s%d %s", u16fmt(DEBUGSTORE_GROUP).c_str(), fk->cxGroupArray - 1, u16fmt(gp->szName).c_str()); // I3481
AddDebugStore(fk, tstr);
}
return CheckForDuplicateGroup(fk, gp);
}
int cmpkeys(const void *key, const void *elem)
{
PFILE_KEY akey;
PFILE_KEY aelem;
int l1, l2;
KMX_WCHAR char_key, char_elem;
akey = (PFILE_KEY)key;
aelem = (PFILE_KEY)elem;
char_key = VKToChar(akey->Key, akey->ShiftFlags);
char_elem = VKToChar(aelem->Key, aelem->ShiftFlags);
if (char_key == char_elem) //akey->Key == aelem->Key)
{
l1 = xstrlen(akey->dpContext); l2 = xstrlen(aelem->dpContext);
if (l1 == l2)
{
if (akey->Line < aelem->Line) return -1;
if (akey->Line > aelem->Line) return 1;
return 0;
}
if (l1 < l2) return 1;
if (l1 > l2) return -1;
}
return(char_key - char_elem); // akey->Key - aelem->Key);
}
KMX_DWORD ProcessGroupFinish(PFILE_KEYBOARD fk)
{
PFILE_GROUP gp;
KMX_DWORD msg;
if (fk->currentGroup == 0xFFFFFFFF) return CERR_None;
// Just got to first group - so nothing to finish yet
gp = &fk->dpGroupArray[fk->currentGroup];
// Finish off the previous group stuff!
if ((msg = ExpandCapsRulesForGroup(fk, gp)) != CERR_None) return msg;
qsort(gp->dpKeyArray, gp->cxKeyArray, sizeof(FILE_KEY), cmpkeys);
return VerifyUnreachableRules(gp);
}
/***************************************
* Store management
*/
KMX_DWORD ProcessStoreLine(PFILE_KEYBOARD fk, PKMX_WCHAR p)
{
PKMX_WCHAR q, pp;
PFILE_STORE sp;
//WCHAR temp[GLOBAL_BUFSIZE];
KMX_DWORD msg;
int i = 0;
pp = p;
if ((q = GetDelimitedString(&p, u"()", GDS_CUTLEAD | GDS_CUTFOLL)) == NULL) return CERR_InvalidStoreLine;
if (*q == *SSN__PREFIX)
{
for (i = 0; StoreTokens[i]; i++)
if (!u16icmp(q, StoreTokens[i])) // I3481
break;
if (!StoreTokens[i]) return CERR_InvalidSystemStore;
}
sp = new FILE_STORE[fk->cxStoreArray + 1];
if (!sp) return CERR_CannotAllocateMemory;
if (fk->dpStoreArray)
{
memcpy(sp, fk->dpStoreArray, sizeof(FILE_STORE) * fk->cxStoreArray);
delete fk->dpStoreArray;
}
fk->dpStoreArray = sp;
sp = &fk->dpStoreArray[fk->cxStoreArray];
sp->line = currentLine;
sp->fIsOption = FALSE;
sp->fIsReserved = FALSE;
sp->fIsStore = FALSE;
sp->fIsDebug = FALSE;
sp->fIsCall = FALSE;
safe_wcsncpy(sp->szName, q, SZMAX_STORENAME);
{
PKMX_WCHAR temp = new KMX_WCHAR[GLOBAL_BUFSIZE];
if ((msg = GetXString(fk, p, u"c\n", temp, GLOBAL_BUFSIZE - 1, (int)(INT_PTR)(p - pp), &p, FALSE, TRUE)) != CERR_None)
{
delete[] temp;
return msg;
}
sp->dwSystemID = i;
sp->dpString = new KMX_WCHAR[u16len(temp) + 1];
u16ncpy(sp->dpString, temp, u16len(temp) + 1); // I3481
delete[] temp;
}
if (xstrlen(sp->dpString) == 1 && *sp->dpString != UC_SENTINEL &&
sp->dwSystemID == 0 && (fk->version >= VERSION_60 || fk->version == 0))
{
// In this case, we want to change behaviour for older versioned keyboards so that
// we don't mix up named character codes which weren't supported in 5.x
VERIFY_KEYBOARD_VERSION(fk, VERSION_60, CERR_60FeatureOnly_NamedCodes);
// Add a single char store as a defined character constant
if (Uni_IsSurrogate1(*sp->dpString))
CodeConstants->AddCode(Uni_SurrogateToUTF32(sp->dpString[0], sp->dpString[1]), sp->szName, fk->cxStoreArray);
else
CodeConstants->AddCode(sp->dpString[0], sp->szName, fk->cxStoreArray);
CodeConstants->reindex(); // has to be done after every character add due to possible use in another store. // I4982
}
fk->cxStoreArray++; // increment now, because GetXString refers to stores
if (i > 0)
if ((msg = ProcessSystemStore(fk, i, sp)) != CERR_None) return msg;
return CheckForDuplicateStore(fk, sp);
}
KMX_DWORD AddStore(PFILE_KEYBOARD fk, KMX_DWORD SystemID, KMX_WCHAR const * str, KMX_DWORD *dwStoreID)
{
PFILE_STORE sp;
sp = new FILE_STORE[fk->cxStoreArray + 1];
if (!sp) return CERR_CannotAllocateMemory;
if (fk->dpStoreArray)
{
memcpy(sp, fk->dpStoreArray, sizeof(FILE_STORE) * fk->cxStoreArray);
delete fk->dpStoreArray;
}
fk->dpStoreArray = sp;
sp = &fk->dpStoreArray[fk->cxStoreArray];
sp->line = currentLine;
sp->fIsOption = FALSE; // I3686
sp->fIsReserved = (SystemID != TSS_NONE);
sp->fIsStore = FALSE;
sp->fIsDebug = FALSE;
sp->fIsCall = FALSE;
safe_wcsncpy(sp->szName, (PKMX_WCHAR) StoreTokens[SystemID], SZMAX_STORENAME);
sp->dpString = new KMX_WCHAR[u16len(str) + 1];
u16ncpy(sp->dpString, str, u16len(str) + 1); // I3481
sp->dwSystemID = SystemID;
if (dwStoreID) *dwStoreID = fk->cxStoreArray;
fk->cxStoreArray++;
return ProcessSystemStore( fk, SystemID, sp);
}
KMX_DWORD AddDebugStore(PFILE_KEYBOARD fk, KMX_WCHAR const * str)
{
PFILE_STORE sp;
KMX_WCHAR tstr[16];
PKMX_WCHAR p_tstr=tstr;
u16sprintf(tstr, _countof(tstr), L"%d", currentLine); // I3481
sp = new FILE_STORE[fk->cxStoreArray + 1];
if (!sp) return CERR_CannotAllocateMemory;
if (fk->dpStoreArray)
{
memcpy(sp, fk->dpStoreArray, sizeof(FILE_STORE) * fk->cxStoreArray);
delete[] fk->dpStoreArray;
}
fk->dpStoreArray = sp;
sp = &fk->dpStoreArray[fk->cxStoreArray];
safe_wcsncpy(sp->szName, (PKMX_WCHAR) str, SZMAX_STORENAME);
sp->dpString = new KMX_WCHAR[u16len(tstr) + 1];
u16ncpy(sp->dpString, tstr, u16len(tstr) + 1); // I3481
sp->line = 0;
sp->fIsOption = FALSE;
sp->fIsReserved = TRUE;
sp->fIsStore = FALSE;
sp->fIsDebug = TRUE;
sp->fIsCall = FALSE;
sp->dwSystemID = TSS_DEBUG_LINE;
fk->cxStoreArray++;
return CERR_None;
}
PKMX_WCHAR pssBuf = NULL;
KMX_DWORD ProcessSystemStore(PFILE_KEYBOARD fk, KMX_DWORD SystemID, PFILE_STORE sp)
{
//WCHAR buf[GLOBAL_BUFSIZE];
int i, j;
KMX_DWORD msg;
PKMX_WCHAR p, q;
KMX_CHAR *pp;
if (!pssBuf) pssBuf = new KMX_WCHAR[GLOBAL_BUFSIZE];
PKMX_WCHAR buf = pssBuf;
switch (SystemID)
{
case TSS_BITMAP:
if ((msg = ImportBitmapFile(fk, sp->dpString, &fk->dwBitmapSize, &fk->lpBitmap)) != CERR_None)
return msg;
break;
case TSS_CALLDEFINITION:
break;
case TSS_CALLDEFINITION_LOADFAILED:
break;
case TSS_CAPSALWAYSOFF:
if (*sp->dpString == u'1') fk->dwFlags |= KF_CAPSALWAYSOFF;
break;
case TSS_CAPSONONLY:
if (*sp->dpString == u'1') fk->dwFlags |= KF_CAPSONONLY;
break;
case TSS_COMPILEDVERSION:
break;
case TSS_COPYRIGHT:
break;
case TSS_DEBUG_LINE:
break;
case TSS_ETHNOLOGUECODE:
VERIFY_KEYBOARD_VERSION(fk, VERSION_60, CERR_60FeatureOnly_EthnologueCode);
if ((msg = ProcessEthnologueStore(sp->dpString)) != CERR_None) return msg; // I2646
break;
case TSS_HOTKEY:
if ((msg = ProcessHotKey(sp->dpString, &fk->dwHotKey)) != CERR_None) return msg;
u16sprintf(buf, GLOBAL_BUFSIZE, L"%d", (int)fk->dwHotKey); // I3481
delete[] sp->dpString;
sp->dpString = new KMX_WCHAR[u16len(buf) + 1];
u16ncpy(sp->dpString, buf, u16len(buf) + 1); // I3481
break;
case TSS_INCLUDECODES:
VERIFY_KEYBOARD_VERSION(fk, VERSION_60, CERR_60FeatureOnly_NamedCodes);
pp = wstrtostr(sp->dpString);
if (!CodeConstants->LoadFile(pp))
{
delete[] pp;
return CERR_CannotLoadIncludeFile;
}
delete[] pp;
CodeConstants->reindex(); // I4982
break;
case TSS_LANGUAGE:
{
char16_t *context = NULL;
KMX_WCHAR sep_c[3] = u", ";
PKMX_WCHAR p_sep_c = sep_c;
q = u16tok(sp->dpString, p_sep_c, &context); // I3481
if (!q) return CERR_InvalidLanguageLine;
i = xatoi(&q);
KMX_WCHAR sep_n[4] = u" c\n";
PKMX_WCHAR p_sep_n = sep_n;
q = u16tok(NULL, p_sep_n, &context); // I3481
if (!q)
{
VERIFY_KEYBOARD_VERSION(fk, VERSION_70, CERR_InvalidLanguageLine);
j = SUBLANGID(i);
i = PRIMARYLANGID(i);
}
else
j = xatoi(&q);
if (i < 1 || j < 1 || i > 0x3FF || j > 0x3F) return CERR_InvalidLanguageLine;
if (i >= 0x200 || j >= 0x20) AddWarning(CWARN_CustomLanguagesNotSupported);
fk->KeyboardID = (KMX_DWORD)MAKELANGID(i, j);
u16sprintf(buf, GLOBAL_BUFSIZE, L"%x %x", i, j); // I3481
delete[] sp->dpString;
sp->dpString = new KMX_WCHAR[u16len(buf) + 1];
u16ncpy(sp->dpString, buf, u16len(buf) + 1); // I3481
break;
}
case TSS_LANGUAGENAME:
break;
case TSS_LAYOUT:
if (fk->KeyboardID == 0) return CERR_LayoutButNoLanguage;
q = sp->dpString;
fk->KeyboardID |= (xatoi(&q) << 16L);
break;
case TSS_MESSAGE:
break;
case TSS_MNEMONIC:
VERIFY_KEYBOARD_VERSION(fk, VERSION_60, CERR_60FeatureOnly_MnemonicLayout);
FMnemonicLayout = atoiW(sp->dpString) == 1;
if (FMnemonicLayout && FindSystemStore(fk, TSS_CASEDKEYS) != NULL) {
// The &CasedKeys system store is not supported for
// mnemonic layouts
return CERR_CasedKeysNotSupportedWithMnemonicLayout;
}
break;
case TSS_NAME:
break;
case TSS_OLDCHARPOSMATCHING:
VERIFY_KEYBOARD_VERSION(fk, VERSION_60, CERR_60FeatureOnly_OldCharPosMatching);
FOldCharPosMatching = atoiW(sp->dpString);
break;
case TSS_SHIFTFREESCAPS:
if (*sp->dpString == u'1') fk->dwFlags |= KF_SHIFTFREESCAPS;
break;
case TSS_VERSION:
if ((fk->dwFlags & KF_AUTOMATICVERSION) == 0) return CERR_VersionAlreadyIncluded;
p = sp->dpString;
if (u16tof (p) < 5.0) {
AddWarning(CWARN_OldVersion);
}
if (u16ncmp(p, u"3.0", 3) == 0) fk->version = VERSION_50; //0x0a0b000n= a.bn
else if (u16ncmp(p, u"3.1", 3) == 0) fk->version = VERSION_50; //all versions < 5.0
else if (u16ncmp(p, u"3.2", 3) == 0) fk->version = VERSION_50; //we compile as if
else if (u16ncmp(p, u"4.0", 3) == 0) fk->version = VERSION_50; //they are 5.0.100.0
else if (u16ncmp(p, u"5.01", 4) == 0) fk->version = VERSION_501;
else if (u16ncmp(p, u"5.0", 3) == 0) fk->version = VERSION_50;
else if (u16ncmp(p, u"6.0", 3) == 0) fk->version = VERSION_60;
else if (u16ncmp(p, u"7.0", 3) == 0) fk->version = VERSION_70;
else if (u16ncmp(p, u"8.0", 3) == 0) fk->version = VERSION_80;
else if (u16ncmp(p, u"9.0", 3) == 0) fk->version = VERSION_90;
else if (u16ncmp(p, u"10.0", 4) == 0) fk->version = VERSION_100;
else if (u16ncmp(p, u"14.0", 4) == 0) fk->version = VERSION_140; // Adds support for #917 -- context() with notany() for KeymanWeb
else if (u16ncmp(p, u"15.0", 4) == 0) fk->version = VERSION_150; // Adds support for U_xxxx_yyyy #2858
else return CERR_InvalidVersion;
if (fk->version < VERSION_60) FOldCharPosMatching = TRUE;
fk->dwFlags &= ~KF_AUTOMATICVERSION;
break;
case TSS_VISUALKEYBOARD:
VERIFY_KEYBOARD_VERSION(fk, VERSION_70, CERR_70FeatureOnly);
{
// Strip path from the store, leaving bare filename only
p = sp->dpString;
char16_t *pp = (char16_t*) u16chr((const PKMX_WCHAR) p, u'\\');
if (!pp) {
pp = p;
} else {
pp++;
}
q = new KMX_WCHAR[u16len(pp) + 1];
u16ncpy(q, pp, u16len(pp) + 1);
// Change compiled reference file extension to .kvk
pp = ( km_kbp_cp *) u16chr(q, 0) - 5;
if (pp > q && u16icmp(pp, u".kvks") == 0) {
pp[4] = 0;
}
delete[] sp->dpString;
sp->dpString = q;
if ((msg = CheckFilenameConsistency(u16fmt(sp->dpString).c_str(), FALSE)) != CERR_None) {
return msg;
}
}
break;
case TSS_KMW_RTL:
case TSS_KMW_HELPTEXT:
VERIFY_KEYBOARD_VERSION(fk, VERSION_70, CERR_70FeatureOnly);
break;
case TSS_KMW_HELPFILE:
case TSS_KMW_EMBEDJS:
VERIFY_KEYBOARD_VERSION(fk, VERSION_70, CERR_70FeatureOnly);
if ((msg = CheckFilenameConsistency(u16fmt(sp->dpString).c_str(), FALSE)) != CERR_None) {
return msg;
}
break;
case TSS_KMW_EMBEDCSS:
VERIFY_KEYBOARD_VERSION(fk, VERSION_90, CERR_90FeatureOnlyEmbedCSS);
if ((msg = CheckFilenameConsistency(u16fmt(sp->dpString).c_str(), FALSE)) != CERR_None) {
return msg;
}
break;
case TSS_TARGETS: // I4504
VERIFY_KEYBOARD_VERSION(fk, VERSION_90, CERR_90FeatureOnlyTargets);
break;
case TSS_WINDOWSLANGUAGES:
{
char16_t *context = NULL;
VERIFY_KEYBOARD_VERSION(fk, VERSION_70, CERR_70FeatureOnly);
size_t szQ = u16len(sp->dpString) * 6 + 1; // I3481
q = new KMX_WCHAR[szQ]; // guaranteed to be enough space for recoding
*q = 0; KMX_WCHAR *r = q;
KMX_WCHAR sep_s[4] = u" ";
PKMX_WCHAR p_sep_s = sep_s;
p = u16tok(sp->dpString, p_sep_s, &context); // I3481
while (p)
{
int n = xatoi(&p);
j = SUBLANGID(n);
i = PRIMARYLANGID(n);
if (i < 1 || j < 1 || i > 0x3FF || j > 0x3F) {
delete[] q;
return CERR_InvalidLanguageLine;
}
u16sprintf(r, szQ - (size_t)(r - q), L"x%04.4x ", n); // I3481
p = u16tok(NULL, sep_s, &context); // I3481
r = (KMX_WCHAR*) u16chr(q, 0); // I3481
}
delete[] sp->dpString;
if (*q) *((KMX_WCHAR*) u16chr(q, 0) - 1) = 0; // delete final space - safe because we control the formatting - ugly? scared?
sp->dpString = q;
break;
}
case TSS_COMPARISON:
VERIFY_KEYBOARD_VERSION(fk, VERSION_80, CERR_80FeatureOnly);
break;
case TSS_VKDICTIONARY: // I3438
VERIFY_KEYBOARD_VERSION(fk, VERSION_90, CERR_90FeatureOnlyVirtualKeyDictionary);
break;
case TSS_LAYOUTFILE: // I3483
VERIFY_KEYBOARD_VERSION(fk, VERSION_90, CERR_90FeatureOnlyLayoutFile); // I4140
if ((msg = CheckFilenameConsistency(u16fmt(sp->dpString).c_str(), FALSE)) != CERR_None) {
return msg;
}
// Used by KMW compiler
break;
case TSS_KEYBOARDVERSION: // I4140
VERIFY_KEYBOARD_VERSION(fk, VERSION_90, CERR_90FeatureOnlyKeyboardVersion);
if (!IsValidKeyboardVersion(sp->dpString)) {
return CERR_KeyboardVersionFormatInvalid;
}
break;
case TSS_CASEDKEYS:
if ((msg = VerifyCasedKeys(sp)) != CERR_None) {
return msg;
}
break;
case TSS_BEGIN_NEWCONTEXT:
case TSS_BEGIN_POSTKEYSTROKE:
break;
case TSS_NEWLAYER:
case TSS_OLDLAYER:
break;
default:
return CERR_InvalidSystemStore;
}
return CERR_None;
}
KMX_BOOL IsValidKeyboardVersion(KMX_WCHAR *dpString) { // I4140
/* version format \d+(\.\d+)* e.g. 9.0.3, 1.0, 1.2.3.4, 6.2.1.4.6.4, blank is not allowed */
do {
if (!iswdigit(*dpString)) {
return FALSE;
}
while (iswdigit(*dpString)) {
dpString++;
}
if (*dpString == '.') {
dpString++;
if (!iswdigit(*dpString)) {
return FALSE;
}
}
} while (*dpString != 0);
return TRUE;
}
KMX_BOOL GetFileVersion(KMX_CHAR *filename, KMX_WORD *d1, KMX_WORD *d2, KMX_WORD *d3, KMX_WORD *d4)
{
KMX_CHAR fnbuf[260];
DWORD h;
DWORD sz;
PKMX_STR p;
VS_FIXEDFILEINFO *vffi;
KMX_UINT len;
GetModuleFileName(0, fnbuf, 260);
sz = GetFileVersionInfoSize(fnbuf, &h);
if (sz == 0) return FALSE;
p = new KMX_CHAR[sz];
if (!p) return FALSE;
GetFileVersionInfo(fnbuf, h, sz, p);
VerQueryValue(p, "\\", (void **)&vffi, &len);
*d1 = HIWORD(vffi->dwFileVersionMS);
*d2 = LOWORD(vffi->dwFileVersionMS);
*d3 = HIWORD(vffi->dwFileVersionLS);
*d4 = LOWORD(vffi->dwFileVersionLS);
delete[] p;
return TRUE;
}
KMX_DWORD AddCompilerVersionStore(PFILE_KEYBOARD fk)
{
if(!FShouldAddCompilerVersion) {
return CERR_None;
}
KMX_WCHAR verstr[32];
PKMX_WCHAR p_verstr = verstr;
KMX_WORD d1, d2, d3, d4;
KMX_WORD msg;
GetFileVersion(NULL, &d1, &d2, &d3, &d4);
u16sprintf(verstr, _countof(verstr), L"%d.%d.%d.%d", d1, d2, d3, d4); // I3481
if ((msg = AddStore(fk, TSS_COMPILEDVERSION, verstr)) != CERR_None) return msg;
return CERR_None;
}
/****************************
* Rule lines
*/
KMX_DWORD CheckStatementOffsets(PFILE_KEYBOARD fk, PFILE_GROUP gp, PKMX_WCHAR context, PKMX_WCHAR output, PKMX_WCHAR key) {
PKMX_WCHAR p, q;
int i;
for (p = output; *p; p = incxstr(p)) {
if (*p == UC_SENTINEL) {
if (*(p + 1) == CODE_INDEX) {
int indexStore = *(p + 2) - 1;
int contextOffset = *(p + 3);
for (q = context, i = 1; *q && i < contextOffset; q = incxstr(q), i++);
if (*q == 0) {
if (!gp->fUsingKeys)
// no key in the rule, so offset is past end of context
return CERR_IndexDoesNotPointToAny;
if (i < contextOffset) // I4914
// offset is beyond the key
return CERR_IndexDoesNotPointToAny;
q = key;
}
// find the any
if (*q != UC_SENTINEL || *(q + 1) != CODE_ANY)
return CERR_IndexDoesNotPointToAny;
int anyStore = *(q + 2) - 1;
if (xstrlen(fk->dpStoreArray[indexStore].dpString) < xstrlen(fk->dpStoreArray[anyStore].dpString)) {
AddWarning(CWARN_IndexStoreShort); //TODO: if this fails, then we return FALSE instead of an error
}
} else if (*(p + 1) == CODE_CONTEXTEX) {
int contextOffset = *(p + 2);
if (contextOffset > xstrlen(context))
return CERR_ContextExHasInvalidOffset;
// Due to a limitation in earlier versions of KeymanWeb, the minimum version
// for context() referring to notany() is 14.0. See #917 for details.
if (CompileTarget == CKF_KEYMANWEB) {
for (q = context, i = 1; *q && i < contextOffset; q = incxstr(q), i++);
if (*q == UC_SENTINEL && *(q + 1) == CODE_NOTANY) {
VERIFY_KEYBOARD_VERSION(fk, VERSION_140, CERR_140FeatureOnlyContextAndNotAnyWeb);
}
}
}
}
}
return CERR_None;
}
/**
*Checks that the order of statements in the context matches the specification
* Rule structure: [context] ['+' key] '>' output
* Context structure: [nul] [if()|baselayout()|platform()]+ [char|any|context()|deadkey()|dk()|index()|notany()|outs()]
* Test that nul is first, then if(), baselayout(), platform() statements are before any other content
*/
KMX_BOOL CheckContextStatementPositions(PKMX_WCHAR context) {
KMX_BOOL hadContextChar = FALSE;
for (PKMX_WCHAR p = context; *p; p = incxstr(p)) {
if (*p == UC_SENTINEL) {
switch (*(p + 1)) {
case CODE_NUL:
if (p > context) {
AddWarning(CWARN_NulNotFirstStatementInContext);
}
break;
case CODE_IFOPT:
case CODE_IFSYSTEMSTORE:
if (hadContextChar) {
AddWarning(CWARN_IfShouldBeAtStartOfContext);
}
break;
default:
hadContextChar = TRUE;
}
}
else {
hadContextChar = TRUE;
}
}
return TRUE;
}
/**
* Checks if a use() statement is followed by other content in the output of a rule
*/
KMX_DWORD CheckUseStatementsInOutput(const PFILE_GROUP gp,PKMX_WCHAR output) {
KMX_BOOL hasUse = FALSE;
PKMX_WCHAR p;
for (p = output; *p; p = incxstr(p)) {
if (*p == UC_SENTINEL && *(p + 1) == CODE_USE) {
hasUse = TRUE;
} else if (hasUse) {
AddWarning(CWARN_UseNotLastStatementInRule);
break;
}
}
return CERR_None;
}
/**
* Adds implicit `context` to start of output of rules for readonly groups
*/
KMX_DWORD InjectContextToReadonlyOutput(PKMX_WCHAR pklOut) {
if (pklOut[0] != UC_SENTINEL || pklOut[1] != CODE_CONTEXT) {
if (u16len(pklOut) > GLOBAL_BUFSIZE - 3) {
return CERR_CannotAllocateMemory;
}
memmove(pklOut + 2, pklOut, (u16len(pklOut) + 1) * 2);
pklOut[0] = UC_SENTINEL;
pklOut[1] = CODE_CONTEXT;
}
return CERR_None;
}
/**
* Verifies that a keyboard does not attempt to emit characters or
* other changes to text store when processing a readonly group
*/
KMX_DWORD CheckOutputIsReadonly(const PFILE_KEYBOARD fk, const PKMX_WCHAR output) { // I4867
PKMX_WCHAR p;
for (p = output; *p; p = incxstr(p)) {
if (*p != UC_SENTINEL) {
return CERR_OutputInReadonlyGroup;
}
switch (*(p + 1)) {
case CODE_CALL:
// We cannot be sure that the callee is going to be readonly
// but we have to operate on a trust basis for call() in any
// case, so we'll allow it.
continue;
case CODE_USE:
// We only allow use() of other readonly groups
{
PFILE_GROUP targetGroup = &fk->dpGroupArray[*(p + 2) - 1];
if (!targetGroup->fReadOnly) {
return CERR_CannotUseReadWriteGroupFromReadonlyGroup;
}
}
continue;
case CODE_SETOPT:
case CODE_RESETOPT:
case CODE_SAVEOPT:
// it is okay to set, reset or save keyboard options
// although it's hard to see good use cases for this
continue;
case CODE_SETSYSTEMSTORE:
// it is okay to set system stores; Engine or Core will
// ignore set(&) that are not permissible in the given context
continue;
case CODE_CONTEXT:
// We allow `context` but only as the very first statement in output
if (p == output) {
continue;
}
return CERR_OutputInReadonlyGroup;
default:
// Note: conceptually, CODE_NUL could be transformed to CODE_CONTEXT
// if the context was also empty, but it is probably safest to avoid this,
// given CODE_CONTEXT does what we need anyway
return CERR_StatementNotPermittedInReadonlyGroup;
}
}
return CERR_None;
}
KMX_DWORD ProcessKeyLine(PFILE_KEYBOARD fk, PKMX_WCHAR str, KMX_BOOL IsUnicode)
{
PKMX_WCHAR p, pp;
KMX_DWORD msg;
PFILE_GROUP gp;
PFILE_KEY kp;
PKMX_WCHAR pklIn, pklKey, pklOut;
pklIn = new KMX_WCHAR[GLOBAL_BUFSIZE]; // I2432 - Allocate buffers each line -- slightly slower but safer than keeping a single buffer
pklKey = new KMX_WCHAR[GLOBAL_BUFSIZE];
pklOut = new KMX_WCHAR[GLOBAL_BUFSIZE];
if (!pklIn || !pklKey || !pklOut)
return CERR_CannotAllocateMemory; // forget about the little leak if pklKey or pklOut fail...
__try
{
gp = &fk->dpGroupArray[fk->currentGroup];
pp = str;
if (gp->fUsingKeys) {
if ((msg = GetXString(fk, str, u"+", pklIn, GLOBAL_BUFSIZE - 1, (int)(INT_PTR)(str - pp), &p, TRUE, IsUnicode)) != CERR_None) return msg;
str = p + 1;
if ((msg = GetXString(fk, str, u">", pklKey, GLOBAL_BUFSIZE - 1, (int)(INT_PTR)(str - pp), &p, TRUE, IsUnicode)) != CERR_None) return msg;
if (pklKey[0] == 0) return CERR_ZeroLengthString;
if (xstrlen(pklKey) > 1) AddWarning(CWARN_KeyBadLength);
} else {
if ((msg = GetXString(fk, str, u">", pklIn, GLOBAL_BUFSIZE - 1, (int)(INT_PTR)(str - pp), &p, TRUE, IsUnicode)) != CERR_None) return msg;
if (pklIn[0] == 0) return CERR_ZeroLengthString;
}
str = p + 1;
if ((msg = GetXString(fk, str, u"c\n", pklOut, GLOBAL_BUFSIZE - 1, (int)(INT_PTR)(str - pp), &p, TRUE, IsUnicode)) != CERR_None) return msg;
if (pklOut[0] == 0) return CERR_ZeroLengthString;
CheckContextStatementPositions(pklIn);
// Test index and context offsets in context
if ((msg = CheckStatementOffsets(fk, gp, pklIn, pklOut, pklKey)) != CERR_None) return msg;
// Test that use() statements are not followed by other content
if ((msg = CheckUseStatementsInOutput(gp, pklOut)) != CERR_None) {
return msg; // I4867
}
if (gp->fReadOnly) {
// Ensure no output is made from the rule, and that
// use() statements meet required readonly semantics
if ((msg = CheckOutputIsReadonly(fk, pklOut)) != CERR_None) {
return msg;
}
// Inject `context` to start of output if group is readonly
// to keep the output internally consistent
if ((msg = InjectContextToReadonlyOutput(pklOut)) != CERR_None) {
return msg;
}
}
kp = new FILE_KEY[gp->cxKeyArray + 1];
if (!kp) return CERR_CannotAllocateMemory;
if (gp->dpKeyArray)
{
memcpy(kp, gp->dpKeyArray, gp->cxKeyArray * sizeof(FILE_KEY));
delete gp->dpKeyArray;
}
gp->dpKeyArray = kp;
kp = &gp->dpKeyArray[gp->cxKeyArray];
gp->cxKeyArray++;
kp->dpOutput = new KMX_WCHAR[u16len(pklOut) + 1];
u16ncpy(kp->dpOutput, pklOut, u16len(pklOut) + 1); // I3481
kp->dpContext = new KMX_WCHAR[u16len(pklIn) + 1];
u16ncpy(kp->dpContext, pklIn, u16len(pklIn) + 1); // I3481
kp->Line = currentLine;
// Finished if we are not using keys
if (!gp->fUsingKeys)
{
kp->Key = 0;
kp->ShiftFlags = 0;
return CERR_None;
}
// Expand each rule out into multiple rules - much faster processing at the key hit time
if (*pklKey == 0) return CERR_ZeroLengthString;
if (*pklKey == UC_SENTINEL)
switch (*(pklKey + 1))
{
case CODE_ANY:
kp->ShiftFlags = 0;
if ((msg = ExpandKp(fk, kp, *(pklKey + 2) - 1)) != CERR_None) return msg;
break;
case CODE_EXTENDED:
kp->Key = *(pklKey + 3);
kp->ShiftFlags = *(pklKey + 2);
break;
default:
return CERR_InvalidCodeInKeyPartOfRule;
}
else
{
kp->ShiftFlags = 0;
kp->Key = *pklKey;
}
}
__finally
{
delete pklIn; // I2432 - Allocate buffers each line -- slightly slower but safer than keeping a single buffer
delete pklKey;
delete pklOut;
}
return CERR_None;
}
KMX_DWORD ExpandKp_ReplaceIndex(PFILE_KEYBOARD fk, PFILE_KEY k, KMX_DWORD keyIndex, int nAnyIndex)
{
/* Replace each index(xx,keyIndex) in k->dpOutput with appropriate char as based on nAnyIndex */
PFILE_STORE s;
int i;
PKMX_WCHAR pIndex, pStore;
for (pIndex = k->dpOutput; *pIndex; pIndex = incxstr(pIndex))
{
if (*pIndex == UC_SENTINEL && *(pIndex + 1) == CODE_INDEX && *(pIndex + 3) == keyIndex)
{
s = &fk->dpStoreArray[*(pIndex + 2) - 1];
for (i = 0, pStore = s->dpString; i < nAnyIndex; i++, pStore = incxstr(pStore));
PKMX_WCHAR qStore = incxstr(pStore);
int w = (int)(INT_PTR)(qStore - pStore);
if (w > 4)
{
*pIndex = UC_SENTINEL;
*(pIndex + 1) = CODE_BEEP;
memmove(pIndex + 2, pIndex + 4, u16len(pIndex + 3) * 2);
}
else
{
memcpy(pIndex, pStore, w * 2);
if (w < 4) memmove(pIndex + w, pIndex + 4, u16len(pIndex + 3) * 2);
}
}
}
return CERR_None;
}
KMX_DWORD ExpandKp(PFILE_KEYBOARD fk, PFILE_KEY kpp, KMX_DWORD storeIndex)
{
PFILE_KEY k;
PKMX_WCHAR pn;
KMX_DWORD nchrs, n;
int keyIndex;
PFILE_STORE sp = &fk->dpStoreArray[storeIndex];
PFILE_GROUP gp = &fk->dpGroupArray[fk->currentGroup];
PKMX_WCHAR dpContext = kpp->dpContext;
PKMX_WCHAR dpOutput = kpp->dpOutput;
nchrs = xstrlen(sp->dpString);
pn = sp->dpString;
keyIndex = xstrlen(dpContext) + 1;
/*
Now we change them to plain characters in the output in multiple rules,
and set the keystroke to the appropriate character in the store.
*/
k = new FILE_KEY[gp->cxKeyArray + nchrs - 1];
if (!k) return CERR_CannotAllocateMemory;
memcpy(k, gp->dpKeyArray, gp->cxKeyArray * sizeof(FILE_KEY));
kpp = &k[(INT_PTR)(kpp - gp->dpKeyArray)];
delete gp->dpKeyArray;
gp->dpKeyArray = k;
gp->cxKeyArray += nchrs - 1;
for (k = kpp, n = 0, pn = sp->dpString; *pn; pn = incxstr(pn), k++, n++)
{
k->dpContext = new KMX_WCHAR[u16len(dpContext) + 1];
k->dpOutput = new KMX_WCHAR[u16len(dpOutput) + 1];
u16ncpy(k->dpContext, dpContext, u16len(dpContext) + 1); // copy the context. // I3481
u16ncpy(k->dpOutput, dpOutput, u16len(dpOutput) + 1); // copy the output.
if (*pn == UC_SENTINEL)
{
switch (*(pn + 1))
{
case CODE_EXTENDED:
k->Key = *(pn + 3); // set the key to store offset.
k->ShiftFlags = *(pn + 2);
break;
default:
return CERR_CodeInvalidInKeyStore;
}
}
else
{
k->Key = *pn; // set the key to store offset.
k->ShiftFlags = 0;
}
k->Line = kpp->Line;
ExpandKp_ReplaceIndex(fk, k, keyIndex, n);
}
delete dpContext;
delete dpOutput;
return CERR_None;
}
PKMX_WCHAR GetDelimitedString(PKMX_WCHAR *p, KMX_WCHAR const * Delimiters, KMX_WORD Flags)
{
PKMX_WCHAR q, r;
;
KMX_WCHAR dOpen, dClose;
dOpen = *Delimiters; dClose = *(Delimiters + 1);
q = *p;
while (iswspace(*q)) q++; //***QUERY
if (*q != dOpen) return NULL;
q++;
r = xstrchr(q, &dClose); // Find closing delimiter
if (!r) return NULL;
if (Flags & GDS_CUTLEAD)
while (iswspace(*q)) q++; // cut off leading spaces
if (Flags & GDS_CUTFOLL)
if (!iswspace(*(r - 1))) *r = 0;
else
{
r--; // Cut off following spaces
while (iswspace(*r) && r > q) r--;
r++;
*r = 0; r = xstrchr((r + 1), &dClose);
}
else *r = 0;
r++; while (iswspace(*r)) r++; // Ignore spaces after the close
if (*r == 0) r--; // Safety for terminating strings.
*p = r; // Update pointer position
return q; // Return delimited string
}
LinePrefixType GetLinePrefixType(PKMX_WCHAR *p)
{
PKMX_WCHAR s = *p;
while (iswspace(*s)) s++;
PKMX_WCHAR q = s;
if (*s != '$') return lptNone;
/* I1569 - fix named constants at the start of the line */
s++;
while (__iswcsym(*s)) s++;
if (*s != ':') return lptNone;
if (u16nicmp(q, u"$keyman:", 8) == 0)
{
*p += 8;
return lptKeymanAndKeymanWeb;
}
if (u16nicmp(q, u"$keymanweb:", 11) == 0)
{
*p += 11;
return lptKeymanWebOnly;
}
if (u16nicmp(q, u"$keymanonly:", 12) == 0)
{
*p += 12;
return lptKeymanOnly;
}
return lptOther;
}
int LineTokenType(PKMX_WCHAR *str)
{
int i;
size_t l;
PKMX_WCHAR p = *str;
LinePrefixType lpt = GetLinePrefixType(&p);
if (lpt == lptOther) return T_BLANK;
/* Test KeymanWeb, Keyman and KeymanOnly prefixes */
if (CompileTarget == CKF_KEYMAN && lpt == lptKeymanWebOnly) return T_BLANK;
if (CompileTarget == CKF_KEYMANWEB && lpt == lptKeymanOnly) return T_BLANK;
while (iswspace(*p)) p++;
if (u16chr(LineTokens[0], towupper(*p)))
for (i = 0; i <= T_W_END - T_W_START; i++)
{
l = u16len(LineTokens[i + 1]);
if (u16nicmp(p, LineTokens[i + 1], l) == 0)
{
p += l; while (iswspace(*p)) p++; *str = p;
return i + T_W_START;
}
}
switch (towupper(*p))
{
case 'C':
if (iswspace(*(p + 1))) return T_COMMENT;
break;
case 0:
return T_BLANK;
default:
if (u16chr(u"\"aAbBlLpPnN[OoxXdD0123456789\'+UuiI$", *p)) // I4784
{
*str = p;
return T_KEYTOKEY;
}
}
return T_UNKNOWN;
}
KMX_WCHAR const * DeadKeyChars =
u"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_";
KMX_BOOL strvalidchrs(PKMX_WCHAR q, KMX_WCHAR const * chrs)
{
for (; *q; q++)
if (!u16chr(chrs, *q)) return FALSE;
return TRUE;
}
KMX_DWORD GetXString(PFILE_KEYBOARD fk, PKMX_WCHAR str, KMX_WCHAR const * token, PKMX_WCHAR output, int max, int offset, PKMX_WCHAR *newp, int isVKey, int isUnicode)
{
KMX_DWORD err;
PKMX_WCHAR p = str, q, r;
int type, mx = 0, n, n1, n2, tokenFound = FALSE, z, sFlag = 0, j;
KMX_DWORD i;
KMX_BOOL finished = FALSE;
KMX_WCHAR c;
PKMX_WCHAR tstr = NULL;
int tstrMax = 0;
tstr = new KMX_WCHAR[max]; // I2432 - Allocate buffers each line -- slightly slower but safer than keeping a single buffer - GetXString is re-entrant with if()
tstrMax = max;
__try
{
*tstr = 0;
*output = 0;
p = str;
do
{
tokenFound = FALSE;
while (iswspace(*p) && !u16chr(token, *p)) p++;
if (!*p) break;
ErrChr = (int)(INT_PTR)(p - str) + offset + 1;
/*
char *tokenTypes[] = {
"clearcontext", "deadkey", "context", "return", "switch",
"index", "outs", "beep", "nul", "use", "any", "fix", "dk", "k_", "x", "d", "c",
"[", "]" };
*/
switch (towupper(*p))
{
case 'X':
case 'D': type = 0; break; // xFF, d130: chars, deadkey(n)
case '\"': type = 1; break; // "xxxx": chars
case '\'': type = 2; break; // 'xxxx': chars
case 'A': type = 3; break; // any(s)
case 'B': type = 4; break; // beep, baselayout (synonym for if(&baselayout)) // I3430
case 'I': type = 5; break; // index(s,n), if
case 'O': type = 6; break; // outs(s)
case 'C': type = 7; break; // context, comments, clearcontext, call(s)
case 'N': type = 8; break; // nul, notany
case 'U': type = 9; break; // use(g)
case 'R': type = 10; break; // return, reset
case '[': type = 11; break; // start of vkey section
//case ']': type = 12; break; // end of vkey section
//case 'K': type = 13; break; // virtual key name or "key"
case 'S': type = 14; break; // switch, set, save
case 'F': type = 15; break; // fix (synonym for clearcontext)
case '$': type = 16; break; // named code constants
case 'P': type = 17; break; // platform (synonym for if(&platform)) // I3430
case 'L': type = 18; break; // layer (synonym for set(&layer)) // I3437
case '.': type = 19; break; // .. allows us to specify ranges -- either vkeys or characters
default:
if (iswdigit(*p)) type = 0; // octal number
else type = 99; // error!
}
if (u16chr(token, *p)) tokenFound = TRUE;
switch (type)
{
case 99:
if (tokenFound) break;
{
PKMX_WCHAR p_ErrExtra =strtowstr(ErrExtra);
u16sprintf(p_ErrExtra,_countof(ErrExtra),L"token: %c",(int)*p);
}
return CERR_InvalidToken;
case 0:
if (u16nicmp(p, u"deadkey", z = 7) == 0 ||
u16nicmp(p, u"dk", z = 2) == 0)
{
p += z;
q = GetDelimitedString(&p, u"()", GDS_CUTLEAD | GDS_CUTFOLL);
if (!q || !*q) return CERR_InvalidDeadkey;
KMX_DWORD n = fk->cxDeadKeyArray;
tstr[mx++] = UC_SENTINEL;
tstr[mx++] = CODE_DEADKEY;
if (!strvalidchrs(q, DeadKeyChars)) return CERR_InvalidDeadkey;
tstr[mx++] = GetDeadKey(fk, q); //atoiW(q); 7-5-01: named deadkeys
tstr[mx] = 0;
}
else
{
n = xatoi(&p);
if (*p != '\0' && !iswspace(*p)) return CERR_InvalidValue;
if ((err = UTF32ToUTF16(n, &n1, &n2)) != CERR_None) return err;
tstr[mx++] = n1;
if (n2 >= 0) tstr[mx++] = n2;
tstr[mx] = 0;
}
continue;
case 1:
q = (PKMX_WCHAR) u16chr(p + 1, '\"');
if (!q) return CERR_UnterminatedString;
if ((INT_PTR)(q - p) - 1 + mx > max) return CERR_UnterminatedString;
if (sFlag) return CERR_StringInVirtualKeySection;
u16ncat(tstr, p + 1, (INT_PTR)(q - p) - 1); // I3481
mx += (int)(INT_PTR)(q - p) - 1;
tstr[mx] = 0;
p = q + 1;
continue;
case 2:
q = (PKMX_WCHAR) u16chr(p + 1, '\'');
if (!q) return CERR_UnterminatedString;
if ((INT_PTR)(q - p) - 1 + mx > max) return CERR_UnterminatedString;
if (sFlag) return CERR_StringInVirtualKeySection;
u16ncat(tstr, p + 1, (INT_PTR)(q - p) - 1); // I3481
mx += (int)(INT_PTR)(q - p) - 1;
tstr[mx] = 0;
p = q + 1;
continue;
case 3:
if (u16nicmp(p, u"any", 3) != 0) return CERR_InvalidToken;
if (sFlag) return CERR_AnyInVirtualKeySection;
p += 3;
q = GetDelimitedString(&p, u"()", GDS_CUTLEAD | GDS_CUTFOLL);
if (!q || !*q) return CERR_InvalidAny;
for (i = 0; i < fk->cxStoreArray; i++)
{
if (u16icmp(q, fk->dpStoreArray[i].szName) == 0) break;
}
if (i == fk->cxStoreArray) return CERR_StoreDoesNotExist;
if (!*fk->dpStoreArray[i].dpString) return CERR_ZeroLengthString;
CheckStoreUsage(fk, i, TRUE, FALSE, FALSE);
tstr[mx++] = UC_SENTINEL;
tstr[mx++] = CODE_ANY;
tstr[mx++] = (KMX_WCHAR)i + 1; // store to index + 1, avoids End-of-string
tstr[mx] = 0;
continue;
case 4:
if (u16nicmp(p, u"beep", 4) == 0)
{
if (sFlag) return CERR_BeepInVirtualKeySection;
p += 4;
tstr[mx++] = UC_SENTINEL;
tstr[mx++] = CODE_BEEP;
tstr[mx] = 0;
}
else if (u16nicmp(p, u"baselayout", 10) == 0) // I3430
{
VERIFY_KEYBOARD_VERSION(fk, VERSION_90, CERR_90FeatureOnly_IfSystemStores);
if (sFlag) return CERR_InvalidInVirtualKeySection;
p += 10;
q = GetDelimitedString(&p, u"()", GDS_CUTLEAD | GDS_CUTFOLL);
if (!q || !*q) return CERR_InvalidToken;
err = process_baselayout(fk, q, tstr, &mx);
if (err != CERR_None) return err;
}
else
return CERR_InvalidToken;
continue;
case 5:
if (u16nicmp(p, u"if", 2) == 0)
{
VERIFY_KEYBOARD_VERSION(fk, VERSION_80, CERR_80FeatureOnly);
if (sFlag) return CERR_InvalidInVirtualKeySection;
p += 2;
q = GetDelimitedString(&p, u"()", GDS_CUTLEAD | GDS_CUTFOLL);
if (!q || !*q) return CERR_InvalidIf;
err = process_if(fk, q, tstr, &mx);
if (err != CERR_None) return err;
}
else
{
if (u16nicmp(p, u"index", 5) != 0) return CERR_InvalidToken;
if (sFlag) return CERR_IndexInVirtualKeySection;
p += 5;
q = GetDelimitedString(&p, u"()", GDS_CUTLEAD | GDS_CUTFOLL);
if (!q || !*q) return CERR_InvalidIndex;
{
char16_t *context = NULL;
KMX_WCHAR sep_com[3] = u" ,";
PKMX_WCHAR p_sep_com = sep_com;
r = u16tok(q, p_sep_com, &context); // I3481
if (!r) return CERR_InvalidIndex;
for (i = 0; i < fk->cxStoreArray; i++)
{
if (u16icmp(r, fk->dpStoreArray[i].szName) == 0) break;
}
if (i == fk->cxStoreArray) return CERR_StoreDoesNotExist;
CheckStoreUsage(fk, i, TRUE, FALSE, FALSE);
r = u16tok(NULL, p_sep_com, &context); // I3481
if (!r) return CERR_InvalidIndex;
}
tstr[mx++] = UC_SENTINEL;
tstr[mx++] = CODE_INDEX;
tstr[mx++] = (KMX_WCHAR)i + 1; // avoid EOS for stores
tstr[mx++] = atoiW(r); // character offset of original any.
tstr[mx] = 0;
}
continue;
case 6:
if (u16nicmp(p, u"outs", 4) != 0) return CERR_InvalidToken;
if (sFlag) return CERR_OutsInVirtualKeySection;
p += 4;
q = GetDelimitedString(&p, u"()", GDS_CUTLEAD | GDS_CUTFOLL);
if (!q || !*q) return CERR_InvalidOuts;
for (i = 0; i < fk->cxStoreArray; i++)
{
if (u16icmp(q, fk->dpStoreArray[i].szName) == 0) break;
}
if (i == fk->cxStoreArray) return CERR_StoreDoesNotExist;
CheckStoreUsage(fk, i, TRUE, FALSE, FALSE);
for (q = fk->dpStoreArray[i].dpString; *q; q++)
{
tstr[mx++] = *q;
if (mx >= max - 1) return CERR_BufferOverflow;
}
tstr[mx] = 0;
continue;
case 7:
if (iswspace(*(p + 1))) break; // is a comment -- pre-stripped - so why this test?
if (u16nicmp(p, u"context", 7) == 0)
{
if (sFlag) return CERR_ContextInVirtualKeySection;
p += 7;
q = GetDelimitedString(&p, u"()", GDS_CUTLEAD | GDS_CUTFOLL);
if (q && *q)
{
VERIFY_KEYBOARD_VERSION(fk, VERSION_60, CERR_60FeatureOnly_Contextn);
int n1;
n1 = atoiW(q);
if (n1 < 1 || n1 >= 0xF000) return CERR_InvalidToken;
tstr[mx++] = UC_SENTINEL;
tstr[mx++] = CODE_CONTEXTEX;
tstr[mx++] = n1;
tstr[mx] = 0;
}
else
{
tstr[mx++] = UC_SENTINEL;
tstr[mx++] = CODE_CONTEXT;
tstr[mx] = 0;
}
}
else if (u16nicmp(p, u"clearcontext", 12) == 0)
{
p += 12;
tstr[mx++] = UC_SENTINEL;
tstr[mx++] = CODE_CLEARCONTEXT;
tstr[mx] = 0;
}
else if (u16nicmp(p, u"call", 4) == 0)
{
VERIFY_KEYBOARD_VERSION(fk, VERSION_501, CERR_501FeatureOnly_Call);
if (sFlag) return CERR_CallInVirtualKeySection;
p += 4;
q = GetDelimitedString(&p, u"()", GDS_CUTLEAD | GDS_CUTFOLL);
if (!q || !*q) return CERR_InvalidCall;
for (i = 0; i < fk->cxStoreArray; i++)
{
if (u16icmp(q, fk->dpStoreArray[i].szName) == 0) break;
}
if (!IsValidCallStore(&fk->dpStoreArray[i])) return CERR_InvalidCall;
CheckStoreUsage(fk, i, FALSE, FALSE, TRUE);
if (i == fk->cxStoreArray) return CERR_StoreDoesNotExist;
tstr[mx++] = UC_SENTINEL;
tstr[mx++] = CODE_CALL;
tstr[mx++] = (KMX_WCHAR)i + 1;
tstr[mx] = 0;
fk->dpStoreArray[i].dwSystemID = TSS_CALLDEFINITION;
}
else
return CERR_InvalidToken;
continue;
case 8:
if (u16nicmp(p, u"notany", 6) == 0)
{
VERIFY_KEYBOARD_VERSION(fk, VERSION_70, CERR_70FeatureOnly)
if (sFlag) return CERR_AnyInVirtualKeySection;
p += 6;
q = GetDelimitedString(&p, u"()", GDS_CUTLEAD | GDS_CUTFOLL);
if (!q || !*q) return CERR_InvalidAny;
for (i = 0; i < fk->cxStoreArray; i++)
{
if (u16icmp(q, fk->dpStoreArray[i].szName) == 0) break;
}
if (i == fk->cxStoreArray) return CERR_StoreDoesNotExist;
CheckStoreUsage(fk, i, TRUE, FALSE, FALSE);
tstr[mx++] = UC_SENTINEL;
tstr[mx++] = CODE_NOTANY;
tstr[mx++] = (KMX_WCHAR)i + 1; // store to index + 1, avoids End-of-string
tstr[mx] = 0;
continue;
}
if (u16nicmp(p, u"nul", 3) != 0) return CERR_InvalidToken;
p += 3;
tstr[mx++] = UC_SENTINEL;
tstr[mx++] = CODE_NUL;
tstr[mx] = 0;
continue;
case 9:
if (u16nicmp(p, u"use", 3) != 0)
{
if (*(p + 1) == '+')
{
n = xatoi(&p);
if (*p != '\0' && !iswspace(*p)) return CERR_InvalidValue;
if ((err = UTF32ToUTF16(n, &n1, &n2)) != CERR_None) return err;
tstr[mx++] = n1;
if (n2 >= 0) tstr[mx++] = n2;
tstr[mx] = 0;
if (!isUnicode) AddWarning(CWARN_UnicodeInANSIGroup);
continue;
}
return CERR_InvalidToken;
}
p += 3;
q = GetDelimitedString(&p, u"()", GDS_CUTLEAD | GDS_CUTFOLL);
if (!q || !*q) return CERR_InvalidUse;
tstr[mx++] = UC_SENTINEL;
tstr[mx++] = CODE_USE;
tstr[mx] = GetGroupNum(fk, q);
if (tstr[mx] == 0) return CERR_GroupDoesNotExist;
tstr[++mx] = 0;
continue;
case 10:
if (u16nicmp(p, u"reset", 5) == 0)
{
VERIFY_KEYBOARD_VERSION(fk, VERSION_80, CERR_80FeatureOnly);
if (sFlag) return CERR_InvalidInVirtualKeySection;
p += 5;
q = GetDelimitedString(&p, u"()", GDS_CUTLEAD | GDS_CUTFOLL);
if (!q || !*q) return CERR_InvalidReset;
err = process_reset(fk, q, tstr, &mx);
if (err != CERR_None) return err;
}
else
{
if (u16nicmp(p, u"return", 6) != 0) return CERR_InvalidToken;
p += 6;
tstr[mx++] = UC_SENTINEL;
tstr[mx++] = CODE_RETURN;
tstr[mx] = 0;
u16ncpy(output, tstr, max); // I3481
output[max - 1] = 0;
return 0;
}
continue;
case 11:
p++; sFlag = ISVIRTUALKEY /* 0 */; finished = FALSE;
//printf("--EXTENDEDSTRING--\n");
do
{
while (iswspace(*p)) p++;
switch (towupper(*p))
{
case 'N':
if (u16nicmp(p, u"NCAPS", 5) == 0)
sFlag |= NOTCAPITALFLAG, p += 5;
else finished = TRUE;
break;
case 'L':
if (u16nicmp(p, u"LALT", 4) == 0)
sFlag |= LALTFLAG, p += 4;
else if (u16nicmp(p, u"LCTRL", 5) == 0)
sFlag |= LCTRLFLAG, p += 5;
else finished = TRUE;
break;
case 'R':
if (u16nicmp(p, u"RALT", 4) == 0)
sFlag |= RALTFLAG, p += 4;
else if (u16nicmp(p, u"RCTRL", 5) == 0)
sFlag |= RCTRLFLAG, p += 5;
else finished = TRUE;
break;
case 'A':
if (u16nicmp(p, u"ALT", 3) == 0)
sFlag |= K_ALTFLAG, p += 3;
else finished = TRUE;
break;
case 'C':
if (u16nicmp(p, u"CTRL", 4) == 0)
sFlag |= K_CTRLFLAG, p += 4;
else if (u16nicmp(p, u"CAPS", 4) == 0)
sFlag |= CAPITALFLAG, p += 4;
else finished = TRUE;
break;
case 'S':
if (u16nicmp(p, u"SHIFT", 5) == 0)
sFlag |= K_SHIFTFLAG, p += 5;
else finished = TRUE;
break;
default:
finished = TRUE;
break;
}
} while (!finished);
if ((sFlag & (LCTRLFLAG | LALTFLAG)) && (sFlag & (RCTRLFLAG | RALTFLAG))) {
AddWarning(CWARN_MixingLeftAndRightModifiers);
}
// If we use chiral modifiers, or we use state keys, and we target web in the keyboard, and we don't manually specify a keyboard version, bump the minimum
// version to 10.0. This makes an assumption that if we are using these features in a keyboard and it has no version specified, that we want to use the features
// in the web target platform, even if there are platform() rules excluding this possibility. In that (rare) situation, the keyboard developer should simply specify
// the &version to be 9.0 or whatever to avoid this behaviour.
if (sFlag & (LCTRLFLAG | LALTFLAG | RCTRLFLAG | RALTFLAG | CAPITALFLAG | NOTCAPITALFLAG | NUMLOCKFLAG | NOTNUMLOCKFLAG | SCROLLFLAG | NOTSCROLLFLAG) &&
CompileTarget == CKF_KEYMANWEB &&
fk->dwFlags & KF_AUTOMATICVERSION) {
VERIFY_KEYBOARD_VERSION(fk, VERSION_100, 0);
}
//printf("sFlag: %x\n", sFlag);
tstr[mx++] = UC_SENTINEL;
tstr[mx++] = CODE_EXTENDED;
tstr[mx++] = sFlag;
while (iswspace(*p)) p++;
q = p;
if (*q == ']')
{
return CERR_InvalidToken; // I3137 - key portion of VK is missing e.g. "[CTRL ALT]", this generates invalid kmx file that can crash Keyman or compiler later on // I3511
}
while (*q != ']')
{
if (*q == '\'' || *q == '"')
{
VERIFY_KEYBOARD_VERSION(fk, VERSION_60, CERR_60FeatureOnly_VirtualCharKey);
if (!FMnemonicLayout) AddWarning(CWARN_VirtualCharKeyWithPositionalLayout);
KMX_WCHAR chQuote = *q;
q++; if (*q == chQuote || *q == '\n' || *q == 0) return CERR_InvalidToken;
tstr[mx - 1] |= VIRTUALCHARKEY;
tstr[mx++] = *q;
q++; if (*q != chQuote) return CERR_InvalidToken;
q++;
while (iswspace(*q)) q++;
if (*q != ']') return CERR_InvalidToken;
break; /* out of while loop */
}
for (j = 0; !iswspace(*q) && *q != ']' && *q != 0; q++, j++);
if (*q == 0) return CERR_InvalidToken;
KMX_WCHAR vkname[SZMAX_VKDICTIONARYNAME]; // I3438
if (j >= SZMAX_VKDICTIONARYNAME) return CERR_InvalidToken;
u16ncpy(vkname, p, j); // I3481
vkname[j] = 0;
if (u16icmp(vkname, u"K_NPENTER") == 0)
i = 5; // I649 - K_NPENTER hack
else
{
for (i = 0; i <= VK__MAX; i++)
{
if (u16icmp(vkname, VKeyNames[i]) == 0 || u16icmp(vkname, VKeyISO9995Names[i]) == 0)
break;
}
}
if (i == VK__MAX + 1)
{
VERIFY_KEYBOARD_VERSION(fk, VERSION_90, CERR_InvalidToken);
i = GetVKCode(fk, vkname); // I3438
if (i == 0)
return CERR_InvalidToken;
}
p = q;
tstr[mx++] = (int)i;
if (FMnemonicLayout && (i <= VK__MAX) && VKeyMayBeVCKey[i]) AddWarning(CWARN_VirtualKeyWithMnemonicLayout); // I3438
while (iswspace(*q)) q++;
}
tstr[mx++] = UC_SENTINEL_EXTENDEDEND;
tstr[mx] = 0;
//printf("--EXTENDEDEND--\n");
p = q + 1;
sFlag = 0;
continue;
case 14:
if (u16nicmp(p, u"set", 3) == 0)
{
VERIFY_KEYBOARD_VERSION(fk, VERSION_80, CERR_80FeatureOnly);
p += 3;
q = GetDelimitedString(&p, u"()", GDS_CUTLEAD | GDS_CUTFOLL);
if (!q || !*q) return CERR_InvalidSet;
err = process_set(fk, q, tstr, &mx);
if (err != CERR_None) return err;
}
else if (u16nicmp(p, u"save", 4) == 0)
{
VERIFY_KEYBOARD_VERSION(fk, VERSION_80, CERR_80FeatureOnly);
p += 4;
q = GetDelimitedString(&p, u"()", GDS_CUTLEAD | GDS_CUTFOLL);
if (!q || !*q) return CERR_InvalidSave;
err = process_save(fk, q, tstr, &mx);
if (err != CERR_None) return err;
}
else
{
if (u16nicmp(p, u"switch", 6) != 0) return CERR_InvalidToken;
p += 6;
q = GetDelimitedString(&p, u"()", GDS_CUTLEAD | GDS_CUTFOLL);
if (!q || !*q) return CERR_InvalidSwitch;
tstr[mx++] = UC_SENTINEL;
tstr[mx++] = CODE_SWITCH;
tstr[mx++] = atoiW(q);
tstr[mx] = 0;
}
continue;
case 15:
if (u16nicmp(p, u"fix", 3) == 0)
{
p += 3;
tstr[mx++] = UC_SENTINEL;
tstr[mx++] = CODE_CLEARCONTEXT;
tstr[mx] = 0;
}
else
return CERR_InvalidToken;
continue;
case 16:
VERIFY_KEYBOARD_VERSION(fk, VERSION_60, CERR_60FeatureOnly_NamedCodes);
q = p + 1;
while (*q && !iswspace(*q)) q++;
c = *q; *q = 0;
n = CodeConstants->GetCode(p + 1, &i);
*q = c;
if (n == 0) return CERR_InvalidNamedCode;
if (i < 0xFFFFFFFFL) CheckStoreUsage(fk, i, TRUE, FALSE, FALSE); // I2993
if (n > 0xFFFF)
{
tstr[mx++] = Uni_UTF32ToSurrogate1(n);
tstr[mx++] = Uni_UTF32ToSurrogate2(n);
}
else
tstr[mx++] = n;
tstr[mx] = 0;
p = q;
continue;
case 17:
if (u16nicmp(p, u"platform", 8) != 0) return CERR_InvalidToken; // I3430
VERIFY_KEYBOARD_VERSION(fk, VERSION_90, CERR_90FeatureOnly_IfSystemStores);
if (sFlag) return CERR_InvalidInVirtualKeySection;
p += 8;
q = GetDelimitedString(&p, u"()", GDS_CUTLEAD | GDS_CUTFOLL);
if (!q || !*q) return CERR_InvalidToken;
err = process_platform(fk, q, tstr, &mx);
if (err != CERR_None) return err;
continue;
case 18: // I3437
if (u16nicmp(p, u"layer", 5) != 0) return CERR_InvalidToken;
VERIFY_KEYBOARD_VERSION(fk, VERSION_90, CERR_90FeatureOnly_SetSystemStores);
if (sFlag) return CERR_InvalidInVirtualKeySection;
p += 5;
q = GetDelimitedString(&p, u"()", GDS_CUTLEAD | GDS_CUTFOLL);
if (!q || !*q) return CERR_InvalidToken;
err = process_set_synonym(TSS_LAYER, fk, q, tstr, &mx);
if (err != CERR_None) return err;
continue;
case 19: // #2241
if (*(p + 1) != '.') return CERR_InvalidToken;
if (sFlag) return CERR_InvalidInVirtualKeySection;
p += 2;
err = process_expansion(fk, p, tstr, &mx, max);
if (err != CERR_None) return err;
continue;
default:
return CERR_InvalidToken;
}
if (tokenFound)
{
*newp = p;
u16ncpy(output, tstr, max); // I3481
output[max - 1] = 0;
ErrChr = 0;
return CERR_None;
}
if (mx >= max) return CERR_BufferOverflow;
} while (*p);
if (!*token)
{
*newp = p;
u16ncpy(output, tstr, max); // I3481
output[max - 1] = 0;
ErrChr = 0;
return CERR_None;
}
}
__finally
{
delete[] tstr; // I2432 - Allocate buffers each line -- slightly slower but safer than keeping a single buffer - GetXString is re-entrant with if()
}
return CERR_NoTokensFound;
}
KMX_DWORD process_if_synonym(KMX_DWORD dwSystemID, PFILE_KEYBOARD fk, LPKMX_WCHAR q, LPKMX_WCHAR tstr, int *mx); // I3430
KMX_DWORD process_baselayout(PFILE_KEYBOARD fk, LPKMX_WCHAR q, LPKMX_WCHAR tstr, int *mx) // I3430
{
/* baselayout(<XString+outs>) */
return process_if_synonym(TSS_BASELAYOUT, fk, q, tstr, mx);
}
KMX_DWORD process_platform(PFILE_KEYBOARD fk, LPKMX_WCHAR q, LPKMX_WCHAR tstr, int *mx) // I3430
{
/* platform(<XString+outs>) */
return process_if_synonym(TSS_PLATFORM, fk, q, tstr, mx);
}
KMX_DWORD process_if_synonym(KMX_DWORD dwSystemID, PFILE_KEYBOARD fk, LPKMX_WCHAR q, LPKMX_WCHAR tstr, int *mx) // I3430
{
PKMX_WCHAR temp = new KMX_WCHAR[GLOBAL_BUFSIZE];
KMX_DWORD msg;
PKMX_WCHAR r;
if ((msg = GetXString(fk, q, u"", temp, GLOBAL_BUFSIZE - 1, 0, &r, FALSE, TRUE)) != CERR_None)
{
delete temp;
return msg;
}
KMX_DWORD dwStoreID;
if ((msg = AddStore(fk, TSS_COMPARISON, temp, &dwStoreID)) != CERR_None)
{
delete temp;
return msg;
}
tstr[(*mx)++] = UC_SENTINEL;
tstr[(*mx)++] = (KMX_WCHAR)CODE_IFSYSTEMSTORE;
tstr[(*mx)++] = (KMX_WCHAR)(dwSystemID + 1); // I4785
tstr[(*mx)++] = 2;
tstr[(*mx)++] = (KMX_WCHAR)(dwStoreID + 1);
tstr[(*mx)] = 0;
delete[] temp;
return CERR_None;
}
KMX_DWORD process_if(PFILE_KEYBOARD fk, LPKMX_WCHAR q, LPKMX_WCHAR tstr, int *mx) // I3431
{
/* if(<store> <'='|'!='> <XString+outs>) */
KMX_DWORD i, code; KMX_DWORD nnot = FALSE;
LPKMX_WCHAR r = q, s = q;
while (*s && *s != u' ' && *s != u'!' && *s !=u'=') s++;
r = s;
while (*s == u' ') s++;
if (*s == u'!')
{
s++;
nnot = TRUE;
}
if (*s != '=') return CERR_InvalidIf;
s++;
while (*s == ' ') s++;
*r = 0;
r = q;
if (r[0] == '&')
{
VERIFY_KEYBOARD_VERSION( fk, VERSION_90, CERR_90FeatureOnly_IfSystemStores);
for (i = 0; StoreTokens[i]; i++)
{
if (u16icmp(r, StoreTokens[i]) == 0) break;
}
if (!StoreTokens[i]) return CERR_IfSystemStore_NotFound;
code = CODE_IFSYSTEMSTORE;
}
else
{
code = CODE_IFOPT;
for (i = 0; i < fk->cxStoreArray; i++)
{
if (u16icmp(r, fk->dpStoreArray[i].szName) == 0) break;
}
if (i == fk->cxStoreArray) return CERR_StoreDoesNotExist;
CheckStoreUsage(fk, i, FALSE, TRUE, FALSE);
}
PKMX_WCHAR temp = new KMX_WCHAR[GLOBAL_BUFSIZE];
KMX_DWORD msg;
if ((msg = GetXString(fk, s, u"", temp, GLOBAL_BUFSIZE - 1, 0, &r, FALSE, TRUE)) != CERR_None)
{
delete[] temp;
return msg;
}
KMX_DWORD dwStoreID;
if ((msg = AddStore(fk, TSS_COMPARISON, temp, &dwStoreID)) != CERR_None)
{
delete[] temp;
return msg;
}
tstr[(*mx)++] = UC_SENTINEL;
tstr[(*mx)++] = (KMX_WCHAR)code;
tstr[(*mx)++] = (KMX_WCHAR)(i + 1);
tstr[(*mx)++] = nnot ? 1 : 2;
tstr[(*mx)++] = (KMX_WCHAR)(dwStoreID + 1);
tstr[(*mx)] = 0;
return CERR_None;
}
KMX_DWORD process_reset(PFILE_KEYBOARD fk, LPKMX_WCHAR q, LPKMX_WCHAR tstr, int *mx)
{
/* reset(<store>) */
KMX_DWORD i;
for (i = 0; i < fk->cxStoreArray; i++)
{
if (u16icmp(q, fk->dpStoreArray[i].szName) == 0) break;
}
if (i == fk->cxStoreArray) return CERR_StoreDoesNotExist;
CheckStoreUsage(fk, i, FALSE, TRUE, FALSE);
tstr[(*mx)++] = UC_SENTINEL;
tstr[(*mx)++] = CODE_RESETOPT;
tstr[(*mx)++] = (KMX_WCHAR)(i + 1);
tstr[(*mx)] = 0;
return CERR_None;
}
KMX_DWORD process_expansion(PFILE_KEYBOARD fk, LPKMX_WCHAR q, LPKMX_WCHAR tstr, int *mx, int max) {
KMX_BOOL isVKey = FALSE;
KMX_WORD BaseKey=0, BaseShiftFlags=0;
KMX_DWORD BaseChar=0;
if (*mx == 0) {
return CERR_ExpansionMustFollowCharacterOrVKey;
}
LPKMX_WCHAR p = &tstr[*mx];
p = decxstr(p, tstr);
if (*p == UC_SENTINEL) {
if (*(p + 1) != CODE_EXTENDED) {
return CERR_ExpansionMustFollowCharacterOrVKey;
}
isVKey = TRUE;
BaseKey = *(p + 3);
BaseShiftFlags = *(p + 2);
}
else {
BaseChar = Uni_UTF16ToUTF32(p);
}
// Look ahead at next element
KMX_WCHAR temp[GLOBAL_BUFSIZE];
PKMX_WCHAR r = NULL;
KMX_DWORD msg;
if ((msg = GetXString(fk, q, u"", temp, _countof(temp) - 1, 0, &r, FALSE, TRUE)) != CERR_None)
{
return msg;
}
KMX_WORD HighKey, HighShiftFlags;
KMX_DWORD HighChar;
switch(temp[0]) {
case 0:
return isVKey ? CERR_VKeyExpansionMustBeFollowedByVKey : CERR_CharacterExpansionMustBeFollowedByCharacter;
case UC_SENTINEL:
// Verify that range is valid virtual key range
if(!isVKey) {
return CERR_CharacterExpansionMustBeFollowedByCharacter;
}
if (temp[1] != CODE_EXTENDED) {
return CERR_VKeyExpansionMustBeFollowedByVKey;
}
HighKey = temp[3], HighShiftFlags = temp[2];
if (HighShiftFlags != BaseShiftFlags) {
return CERR_VKeyExpansionMustUseConsistentShift;
}
if (HighKey <= BaseKey) {
return CERR_ExpansionMustBePositive;
}
// Verify space in buffer
if (*mx + (HighKey - BaseKey) * 5 + 1 >= max) return CERR_BufferOverflow;
// Inject an expansion.
for (BaseKey++; BaseKey < HighKey; BaseKey++) {
// < HighKey because caller will add HighKey to output
tstr[(*mx)++] = UC_SENTINEL;
tstr[(*mx)++] = CODE_EXTENDED;
tstr[(*mx)++] = BaseShiftFlags;
tstr[(*mx)++] = BaseKey;
tstr[(*mx)++] = UC_SENTINEL_EXTENDEDEND;
}
tstr[*mx] = 0;
break;
default:
// Verify that range is a valid character range
if (isVKey) {
return CERR_VKeyExpansionMustBeFollowedByVKey;
}
HighChar = Uni_UTF16ToUTF32(temp);
if (HighChar <= BaseChar) {
return CERR_ExpansionMustBePositive;
}
// Inject an expansion.
for (BaseChar++; BaseChar < HighChar; BaseChar++) {
// < HighChar because caller will add HighChar to output
if (Uni_IsSMP(BaseChar)) {
// We'll test on each char to avoid complex calculations crossing SMP boundary
if (*mx + 3 >= max) return CERR_BufferOverflow;
tstr[(*mx)++] = (KMX_WCHAR) Uni_UTF32ToSurrogate1(BaseChar);
tstr[(*mx)++] = (KMX_WCHAR) Uni_UTF32ToSurrogate2(BaseChar);
}
else {
if (*mx + 2 >= max) return CERR_BufferOverflow;
tstr[(*mx)++] = (KMX_WCHAR) BaseChar;
}
}
tstr[*mx] = 0;
}
return CERR_None;
}
KMX_DWORD process_set_synonym(KMX_DWORD dwSystemID, PFILE_KEYBOARD fk, LPKMX_WCHAR q, LPKMX_WCHAR tstr, int *mx) // I3437
{
/* set(<store> <'='> <XString+outs>), layer */
KMX_DWORD code = CODE_SETSYSTEMSTORE;
PKMX_WCHAR temp = new KMX_WCHAR[GLOBAL_BUFSIZE], r;
KMX_DWORD msg;
if ((msg = GetXString(fk, q, u"", temp, GLOBAL_BUFSIZE - 1, 0, &r, FALSE, TRUE)) != CERR_None)
{
delete[] temp;
return msg;
}
KMX_DWORD dwStoreID;
msg = AddStore(fk, TSS_COMPARISON, temp, &dwStoreID);
delete[] temp;
if (msg != CERR_None) return msg;
tstr[(*mx)++] = UC_SENTINEL;
tstr[(*mx)++] = (KMX_WCHAR)CODE_SETSYSTEMSTORE;
tstr[(*mx)++] = (KMX_WCHAR)(dwSystemID + 1);
tstr[(*mx)++] = (KMX_WCHAR)(dwStoreID + 1);
tstr[(*mx)] = 0;
return CERR_None;
}
KMX_DWORD process_set(PFILE_KEYBOARD fk, LPKMX_WCHAR q, LPKMX_WCHAR tstr, int *mx)
{
/* set(<store> <'='> <XString+outs> */
LPKMX_WCHAR r = q, s = q; // I3440
while (*s && *s != u' ' && *s != u'=') s++;
r = s;
while (*s == u' ') s++;
if (*s != '=') return CERR_InvalidSet;
s++;
while (*s == ' ') s++;
*r = 0;
r = q;
KMX_DWORD i, code;
if (r[0] == '&')
{
VERIFY_KEYBOARD_VERSION((PFILE_KEYBOARD) fk, VERSION_90, CERR_90FeatureOnly_SetSystemStores); // I3437
for (i = 0; StoreTokens[i]; i++)
{
if (u16icmp(r, StoreTokens[i]) == 0) break;
}
if (!StoreTokens[i]) return CERR_SetSystemStore_NotFound;
code = CODE_SETSYSTEMSTORE;
}
else
{
char16_t *context = NULL;
KMX_WCHAR sep_eq[3] = u" =";
PKMX_WCHAR p_sep_eq = sep_eq;
LPKMX_WCHAR r = u16tok(q, sep_eq, &context); // I3481
for (i = 0; i < fk->cxStoreArray; i++)
{
if (u16icmp(r, fk->dpStoreArray[i].szName) == 0) break;
}
if (i == fk->cxStoreArray) return CERR_StoreDoesNotExist;
CheckStoreUsage(fk, i, FALSE, TRUE, FALSE);
code = CODE_SETOPT;
}
PKMX_WCHAR temp = new KMX_WCHAR[GLOBAL_BUFSIZE];
KMX_DWORD msg;
//r = wcstok(NULL, L" =");
if ((msg = GetXString(fk, s, u"", temp, GLOBAL_BUFSIZE - 1, 0, &r, FALSE, TRUE)) != CERR_None)
{
delete[] temp;
return msg;
}
KMX_DWORD dwStoreID;
msg = AddStore(fk, TSS_COMPARISON, temp, &dwStoreID);
delete[] temp;
if (msg != CERR_None) return msg;
tstr[(*mx)++] = UC_SENTINEL;
tstr[(*mx)++] = (KMX_WCHAR)code;
tstr[(*mx)++] = (KMX_WCHAR)(i + 1);
tstr[(*mx)++] = (KMX_WCHAR)(dwStoreID + 1);
tstr[(*mx)] = 0;
return CERR_None;
}
KMX_DWORD process_save(PFILE_KEYBOARD fk, LPKMX_WCHAR q, LPKMX_WCHAR tstr, int *mx)
{
/* save(<store>) */
KMX_DWORD i;
for (i = 0; i < fk->cxStoreArray; i++)
{
if (u16icmp(q, fk->dpStoreArray[i].szName) == 0) break;
}
if (i == fk->cxStoreArray) return CERR_StoreDoesNotExist;
CheckStoreUsage(fk, i, FALSE, TRUE, FALSE);
tstr[(*mx)++] = UC_SENTINEL;
tstr[(*mx)++] = CODE_SAVEOPT;
tstr[(*mx)++] = (KMX_WCHAR)(i + 1);
tstr[(*mx)] = 0;
return CERR_None;
}
int xatoi(PKMX_WCHAR *p)
{
PKMX_WCHAR endptr;
int n;
switch (towupper(**p))
{
case 'U':
(*p)++;
if (**p != '+') return 0;
(*p)++;
n = (int)u16tol(*p, &endptr, 16);
*p = endptr;
break;
case 'X':
(*p)++;
n = (int)u16tol(*p, &endptr, 16);
*p = endptr;
break;
case 'D':
(*p)++;
n = (int)u16tol(*p, &endptr, 10);
*p = endptr;
break;
default:
n = (int)u16tol(*p, &endptr, 8);
*p = endptr;
break;
}
return n;
}
int GetGroupNum(PFILE_KEYBOARD fk, PKMX_WCHAR p)
{
PFILE_GROUP gp;
KMX_DWORD i;
for (i = 0, gp = fk->dpGroupArray; i < fk->cxGroupArray; gp++, i++)
{
if (u16icmp(gp->szName, p) == 0) return i + 1;
}
return 0;
}
KMX_DWORD ProcessEthnologueStore(PKMX_WCHAR p) // I2646
{
KMX_DWORD res = CERR_None;
PKMX_WCHAR q = NULL;
while (*p)
{
while (u16chr(u" ,;", *p))
{
if (*p != ' ') res = CWARN_PunctuationInEthnologueCode;
p++;
}
if (q == p) return CERR_InvalidEthnologueCode;
if (*p)
{
for (int i = 0; i < 3; i++)
{
if (!iswalpha(*p)) return CERR_InvalidEthnologueCode;
p++;
}
}
q = p;
}
return res;
}
#define K_HOTKEYSHIFTFLAGS (K_SHIFTFLAG | K_CTRLFLAG | K_ALTFLAG | ISVIRTUALKEY)
KMX_DWORD ProcessHotKey(PKMX_WCHAR p, KMX_DWORD *hk)
{
PKMX_WCHAR q, r;
KMX_DWORD sFlag;
int j, i;
*hk = 0;
if (*p == UC_SENTINEL && *(p + 1) == CODE_EXTENDED) {
KMX_WORD Key = *(p + 3);
KMX_WORD ShiftFlags = *(p + 2);
// Convert virtual key to hotkey (different bitflags)
if (ShiftFlags & ~K_HOTKEYSHIFTFLAGS) {
AddWarning(CWARN_HotkeyHasInvalidModifier);
}
if (ShiftFlags & K_SHIFTFLAG) *hk |= HK_SHIFT;
if (ShiftFlags & K_CTRLFLAG) *hk |= HK_CTRL;
if (ShiftFlags & K_ALTFLAG) *hk |= HK_ALT;
*hk |= Key;
return CERR_None;
}
q = (PKMX_WCHAR) u16chr(p, '[');
if (q)
{
q++;
sFlag = 0;
do
{
while (iswspace(*q)) q++;
if (u16nicmp(q, u"ALT", 3) == 0) sFlag |= HK_ALT, q += 3;
else if (u16nicmp(q, u"CTRL", 4) == 0) sFlag |= HK_CTRL, q += 4;
else if (u16nicmp(q, u"SHIFT", 5) == 0) sFlag |= HK_SHIFT, q += 5;
else if (towupper(*q) != 'K') return CERR_InvalidToken;
} while (towupper(*q) != 'K');
r = (PKMX_WCHAR) u16chr(q, ']');
if (r)
{
r--;
while (iswspace(*r) && r > q) r--;
r++;
}
else return CERR_NoTokensFound;
j = (int)(INT_PTR)(r - q);
for (i = 0; i <= VK__MAX; i++) // I3438
if (j == (int) u16len(VKeyNames[i]) && u16nicmp(q, VKeyNames[i], j) == 0) break;
if (i == VK__MAX + 1) return CERR_InvalidToken; // I3438
*hk = i | sFlag;
return CERR_None;
}
q = GetDelimitedString(&p, u"\"\"", GDS_CUTLEAD | GDS_CUTFOLL);
if (q)
{
if (u16chr(q, '^')) *hk |= HK_CTRL;
if (u16chr(q, '+')) *hk |= HK_SHIFT;
if (u16chr(q, '%')) *hk |= HK_ALT;
q = (PKMX_WCHAR) u16chr(q, 0) - 1;
*hk |= *q;
return CERR_None;
}
return CERR_CodeInvalidInThisSection;
}
void SetChecksum(LPKMX_BYTE buf, LPKMX_DWORD CheckSum, KMX_DWORD sz)
{
BuildCRCTable();
*CheckSum = CalculateBufferCRC(buf, sz);
}
KMX_BOOL CheckStoreUsage(PFILE_KEYBOARD fk, int storeIndex, KMX_BOOL fIsStore, KMX_BOOL fIsOption, KMX_BOOL fIsCall)
{
PFILE_STORE sp = &fk->dpStoreArray[storeIndex];
if (fIsStore && !sp->fIsStore)
{
if (sp->fIsDebug || sp->fIsOption || sp->fIsReserved || sp->fIsCall)
AddWarning(CWARN_StoreAlreadyUsedAsOptionOrCall);
sp->fIsStore = TRUE;
}
else if (fIsOption && !sp->fIsOption)
{
if (sp->fIsDebug || sp->fIsStore || sp->fIsReserved || sp->fIsCall)
AddWarning(CWARN_StoreAlreadyUsedAsStoreOrCall);
sp->fIsOption = TRUE;
}
else if (fIsCall && !sp->fIsCall)
{
if (sp->fIsDebug || sp->fIsStore || sp->fIsReserved || sp->fIsOption)
AddWarning(CWARN_StoreAlreadyUsedAsStoreOrOption);
sp->fIsCall = TRUE;
}
return TRUE;
}
KMX_DWORD WriteCompiledKeyboard(PFILE_KEYBOARD fk, FILE* fp_out)
{
PFILE_GROUP fgp;
PFILE_STORE fsp;
PFILE_KEY fkp;
PCOMP_KEYBOARD ck;
PCOMP_GROUP gp;
PCOMP_STORE sp;
PCOMP_KEY kp;
PKMX_BYTE buf;
size_t offset;
size_t size;
KMX_DWORD i, j;
// Calculate how much memory to allocate
size = sizeof(COMP_KEYBOARD) +
fk->cxGroupArray * sizeof(COMP_GROUP) +
fk->cxStoreArray * sizeof(COMP_STORE) +
/*wcslen(fk->szName)*2 + 2 +
wcslen(fk->szCopyright)*2 + 2 +
wcslen(fk->szLanguageName)*2 + 2 +
wcslen(fk->szMessage)*2 + 2 +*/
fk->dwBitmapSize;
for (i = 0, fgp = fk->dpGroupArray; i < fk->cxGroupArray; i++, fgp++)
{
if (FSaveDebug) size += u16len(fgp->szName) * 2 + 2;
size += fgp->cxKeyArray * sizeof(COMP_KEY);
for (j = 0, fkp = fgp->dpKeyArray; j < fgp->cxKeyArray; j++, fkp++)
{
size += u16len(fkp->dpOutput) * 2 + 2;
size += u16len(fkp->dpContext) * 2 + 2;
}
if (fgp->dpMatch) size += u16len(fgp->dpMatch) * 2 + 2;
if (fgp->dpNoMatch) size += u16len(fgp->dpNoMatch) * 2 + 2;
}
for (i = 0; i < fk->cxStoreArray; i++)
{
size += u16len(fk->dpStoreArray[i].dpString) * 2 + 2;
if (FSaveDebug || fk->dpStoreArray[i].fIsOption) size += u16len(fk->dpStoreArray[i].szName) * 2 + 2;
}
buf = new KMX_BYTE[size];
if (!buf) return CERR_CannotAllocateMemory;
memset(buf, 0, size);
ck = (PCOMP_KEYBOARD)buf;
ck->dwIdentifier = FILEID_COMPILED;
ck->dwFileVersion = fk->version;
ck->dwCheckSum = 0; // do checksum afterwards.
ck->KeyboardID = fk->KeyboardID;
ck->IsRegistered = TRUE; // I5135
ck->cxStoreArray = fk->cxStoreArray;
ck->cxGroupArray = fk->cxGroupArray;
ck->StartGroup[0] = fk->StartGroup[0];
ck->StartGroup[1] = fk->StartGroup[1];
ck->dwHotKey = fk->dwHotKey;
ck->dwFlags = fk->dwFlags;
offset = sizeof(COMP_KEYBOARD);
/*ck->dpLanguageName = offset;
wcscpy((PWSTR)(buf + offset), fk->szLanguageName);
offset += wcslen(fk->szLanguageName)*2 + 2;
ck->dpName = offset;
wcscpy((PWSTR)(buf + offset), fk->szName);
offset += wcslen(fk->szName)*2 + 2;
ck->dpCopyright = offset;//
wcscpy((PWSTR)(buf + offset), fk->szCopyright);
offset += wcslen(fk->szCopyright)*2 + 2;
ck->dpMessage = offset;
wcscpy((PWSTR)(buf + offset), fk->szMessage);
offset += wcslen(fk->szMessage)*2 + 2;*/
ck->dpStoreArray = (KMX_DWORD)offset;
sp = (PCOMP_STORE)(buf + offset);
fsp = fk->dpStoreArray;
offset += sizeof(COMP_STORE) * ck->cxStoreArray;
for (i = 0; i < ck->cxStoreArray; i++, sp++, fsp++)
{
sp->dwSystemID = fsp->dwSystemID;
sp->dpString = (KMX_DWORD)offset;
u16ncpy((PKMX_WCHAR)(buf + offset), fsp->dpString, (size - offset) / sizeof(KMX_WCHAR)); // I3481 // I3641
offset += u16len(fsp->dpString) * 2 + 2;
if (FSaveDebug || fsp->fIsOption)
{
sp->dpName = (KMX_DWORD)offset;
u16ncpy((PKMX_WCHAR)(buf + offset), fsp->szName, (size - offset) / sizeof(KMX_WCHAR)); // I3481 // I3641
offset += u16len(fsp->szName) * 2 + 2;
}
else sp->dpName = 0;
}
ck->dpGroupArray = (KMX_DWORD)offset;
gp = (PCOMP_GROUP)(buf + offset);
fgp = fk->dpGroupArray;
offset += sizeof(COMP_GROUP) * ck->cxGroupArray;
for (i = 0; i < ck->cxGroupArray; i++, gp++, fgp++)
{
gp->cxKeyArray = fgp->cxKeyArray;
gp->fUsingKeys = fgp->fUsingKeys;
gp->dpMatch = gp->dpNoMatch = 0;
if (fgp->dpMatch)
{
gp->dpMatch = (KMX_DWORD)offset;
u16ncpy((PKMX_WCHAR)(buf + offset), fgp->dpMatch, (size - offset) / sizeof(KMX_WCHAR)); // I3481 // I3641
offset += u16len(fgp->dpMatch) * 2 + 2;
}
if (fgp->dpNoMatch)
{
gp->dpNoMatch = (KMX_DWORD)offset;
u16ncpy((PKMX_WCHAR)(buf + offset), fgp->dpNoMatch, (size - offset) / sizeof(KMX_WCHAR)); // I3481 // I3641
offset += u16len(fgp->dpNoMatch) * 2 + 2;
}
if (FSaveDebug)
{
gp->dpName = (KMX_DWORD)offset;
u16ncpy((PKMX_WCHAR)(buf + offset), fgp->szName, (size - offset) / sizeof(KMX_WCHAR)); // I3481 // I3641
offset += u16len(fgp->szName) * 2 + 2;
}
else gp->dpName = 0;
gp->dpKeyArray = (KMX_DWORD)offset;
kp = (PCOMP_KEY )(buf + offset);
fkp = fgp->dpKeyArray;
offset += gp->cxKeyArray * sizeof(COMP_KEY);
for (j = 0; j < gp->cxKeyArray; j++, kp++, fkp++)
{
kp->Key = fkp->Key;
if (FSaveDebug) kp->Line = fkp->Line; else kp->Line = 0;
kp->ShiftFlags = fkp->ShiftFlags;
kp->dpOutput = (KMX_DWORD)offset;
u16ncpy((PKMX_WCHAR)(buf + offset), fkp->dpOutput, (size - offset) / sizeof(KMX_WCHAR)); // I3481 // I3641
offset += u16len(fkp->dpOutput) * 2 + 2;
kp->dpContext = (KMX_DWORD)offset;
u16ncpy((PKMX_WCHAR)(buf + offset), fkp->dpContext, (size - offset) / sizeof(KMX_WCHAR)); // I3481 // I3641
offset += u16len(fkp->dpContext) * 2 + 2;
}
}
ck->dwBitmapSize = fk->dwBitmapSize;
ck->dpBitmapOffset = (KMX_DWORD)offset;
memcpy(buf + offset, fk->lpBitmap, fk->dwBitmapSize);
offset += fk->dwBitmapSize;
if (offset != size) {
delete[] buf;
return CERR_SomewhereIGotItWrong;
}
SetChecksum(buf, &ck->dwCheckSum, (KMX_DWORD)size);
KMX_DWORD dwBytesWritten = 0;
dwBytesWritten = fwrite(buf,1, (KMX_DWORD)size , fp_out);
if (dwBytesWritten != size) {
delete[] buf;
return CERR_UnableToWriteFully;
}
delete[] buf;
return CERR_None;
}
KMX_DWORD ReadLine(FILE* fp_in , PKMX_WCHAR wstr, KMX_BOOL PreProcess)
{
KMX_DWORD len;
PKMX_WCHAR p;
KMX_BOOL LineCarry = FALSE, InComment = FALSE;
KMX_DWORD n;
KMX_WCHAR currentQuotes = 0;
KMX_WCHAR str[LINESIZE + 3];
len = fread( str , 1 ,LINESIZE * 2,fp_in);
if (ferror(fp_in) )
return CERR_CannotReadInfile;
len /= 2;
str[len] = 0; auto cur = ftell(fp_in);
fseek(fp_in, 0, SEEK_END);
auto fsize = ftell(fp_in);
fseek(fp_in, cur, SEEK_SET);
if (cur == fsize)
// Always a "\r\n" to the EOF, avoids funny bugs
u16ncat(str, u"\r\n", _countof(str)); // I3481
if (len == 0) return CERR_EndOfFile;
for (p = str, n = 0; n < len; n++, p++)
{
if (currentQuotes != 0)
{
if (*p == L'\n')
{
*p = 0; // I2525
u16ncpy(wstr, str, LINESIZE); // I3481
return (PreProcess ? CERR_None : CERR_UnterminatedString);
}
if (*p == currentQuotes) currentQuotes = 0;
continue;
}
if (InComment) {
if (*p == L'\n') break;
*p = L' ';
continue;
}
if (*p == L'\\') {
LineCarry = TRUE;
*p = L' ';
continue;
}
if (LineCarry)
{
switch (*p)
{
case L' ':
case L'\t':
case L'\r':
*p = L' ';
continue;
case L'\n':
currentLine++;
LineCarry = FALSE;
*p = L' ';
continue;
}
*p = 0; // I2525
u16ncpy(wstr, str, LINESIZE); // I3481
return (PreProcess ? CERR_None : CERR_InvalidLineContinuation);
}
if (*p == L'\n') break;
switch (*p)
{
case L'c':
case L'C':
if ((p == str || iswspace(*(p - 1))) && iswspace(*(p + 1))) {
InComment = TRUE;
*p = L' ';
}
continue;
case L'\r':
case L'\t':
*p = L' ';
continue;
case L'\'':
case L'\"':
currentQuotes = *p;
continue;
}
}
if (n == len)
{
str[LINESIZE - 1] = 0; // I2525
u16ncpy(wstr, str, LINESIZE); // I3481
if (len == LINESIZE)
return (PreProcess ? CERR_None : CERR_LineTooLong);
}
if (*p == L'\n') currentLine++;
fseek(fp_in, -(int)(len * 2 - (INT_PTR)(p - str) * 2 - 2), SEEK_CUR);
p--;
while (p >= str && iswspace(*p)) p--;
p++;
*p++ = L'\n';
*p = 0;
// trim spaces now, why not?
u16ncpy(wstr, str, LINESIZE); // I3481
return CERR_None;
}
KMX_DWORD GetRHS(PFILE_KEYBOARD fk, PKMX_WCHAR p, PKMX_WCHAR buf, int bufsize, int offset, int IsUnicode)
{
PKMX_WCHAR q;
p = (const PKMX_WCHAR) u16chr(p, '>');
if (!p) return CERR_NoTokensFound;
p++;
return GetXString(fk, p, u"c\n", buf, bufsize, offset, &q, TRUE, IsUnicode);
}
void safe_wcsncpy(PKMX_WCHAR out, PKMX_WCHAR in, int cbMax)
{
u16ncpy(out, in, cbMax - 1); // I3481
out[cbMax - 1] = 0;
}
KMX_BOOL IsSameToken(PKMX_WCHAR *p, KMX_WCHAR const * token)
{
PKMX_WCHAR q;
q = *p;
while (iswspace(*q)) q++;
if (u16nicmp(q, token, u16len(token)) == 0)
{
q += u16len(token);
while (iswspace(*q)) q++;
*p = q;
return TRUE;
}
return FALSE;
}
/*
KMX_BOOL IsRelativePath(KMX_CHAR *p)
{
// Relative path (returns TRUE):
// ..\...\BITMAP.BMP
// PATH\BITMAP.BMP
// BITMAP.BMP
// Semi-absolute path (returns FALSE):
// \...\BITMAP.BMP
// Absolute path (returns FALSE):
// C:\...\BITMAP.BMP
// \\SERVER\SHARE\...\BITMAP.BMP
if (*p == '\\') return FALSE;
if (*p && *(p + 1) == ':') return FALSE;
return TRUE;
}*/
KMX_DWORD ImportBitmapFile(PFILE_KEYBOARD fk, PKMX_WCHAR szName, PKMX_DWORD FileSize, PKMX_BYTE *Buf)
{
FILE *fp;
KMX_WCHAR szNewName[260], *p;
if (IsRelativePath(u16fmt(szName).c_str()))
{
PKMX_WCHAR WCompileDir = strtowstr(CompileDir);
u16ncpy(szNewName, WCompileDir, _countof(szNewName)); // I3481
u16ncat(szNewName,szName, _countof(szNewName )); // I3481
}
else
u16ncpy(szNewName, szName, _countof(szNewName)); // I3481
#if defined(_WIN32) || defined(_WIN64)
fp =_wfsopen((wchar_t*)szNewName, L"rb", _SH_DENYWR);
#else
//fp = fopen( ( const PKMX_CHAR) szNewName, "rb");
#endif
if ( fp == NULL)
{
// else if filename.bmp is not in the folder -> attempt to open filename.bmp.bmp !
if ( u16cmp(szNewName+u16len(szNewName)-4, u".bmp") )
u16ncat(szNewName, u".bmp", _countof(szNewName)); // I3481
#if defined(_WIN32) || defined(_WIN64)
fp = _wfsopen((const wchar_t*)szNewName, L"rb", _SH_DENYWR);
#else
fp = fopen(( const PKMX_CHAR) szNewName, "rb");
#endif
if ( fp == NULL)
return CERR_CannotReadBitmapFile;
}
KMX_DWORD msg;
if ((msg = CheckFilenameConsistency(u16fmt(szNewName).c_str(), FALSE)) != CERR_None) {
return msg;
}
fseek(fp, 0, SEEK_END);
*FileSize = ftell(fp);
fseek(fp ,0,SEEK_SET);
if (*FileSize < 0) {
fclose(fp);
return CERR_CannotReadBitmapFile;
}
if (*FileSize < 2) return CERR_CannotReadBitmapFile;
*Buf = new KMX_BYTE[*FileSize];
if (fread(*Buf, 1, *FileSize, fp) < (size_t) *FileSize) {
delete[] * Buf;
*Buf = NULL;
return CERR_CannotReadBitmapFile;
}
fclose(fp);
/* Test for version 7.0 icon support */
if (*((PKMX_CHAR)*Buf) != 'B' && *(((PKMX_CHAR)*Buf) + 1) != 'M') {
VERIFY_KEYBOARD_VERSION(fk, VERSION_70, CERR_70FeatureOnly);
}
return CERR_None;
}
int atoiW(PKMX_WCHAR p)
{
PKMX_STR q = wstrtostr(p);
int i = atoi(q);
delete[] q;
return i;
}
int CheckUTF16(int n)
{
const int res[] = {
0xFDD0, 0xFDD1, 0xFDD2, 0xFDD3, 0xFDD4, 0xFDD5, 0xFDD6, 0xFDD7,
0xFDD8, 0xFDD9, 0xFDDA, 0xFDDB, 0xFDDC, 0xFDDD, 0xFDDE, 0xFDDF,
0xFDE0, 0xFDE1, 0xFDE2, 0xFDE3, 0xFDE4, 0xFDE5, 0xFDE6, 0xFDE7,
0xFDE8, 0xFDE9, 0xFDEA, 0xFDEB, 0xFDEC, 0xFDED, 0xFDEE, 0xFDEF,
0xFFFF, 0xFFFE, 0 };
if (n == 0) return CERR_ReservedCharacter;
for (int i = 0; res[i] > 0; i++)
if (n == res[i])
{
AddWarning(CWARN_ReservedCharacter);
break;
}
return CERR_None;
}
int UTF32ToUTF16(int n, int *n1, int *n2)
{
*n2 = -1;
if (n <= 0xFFFF)
{
*n1 = n;
if (n >= 0xD800 && n <= 0xDFFF) AddWarning(CWARN_UnicodeSurrogateUsed);
return CheckUTF16(*n1);
}
if ((n & 0xFFFF) == 0xFFFF || (n & 0xFFFF) == 0xFFFE) AddWarning(CWARN_ReservedCharacter);
if (n < 0 || n > 0x10FFFF) return CERR_InvalidCharacter;
n = n - 0x10000;
*n1 = (n / 0x400) + 0xD800;
*n2 = (n % 0x400) + 0xDC00;
if ((n = CheckUTF16(*n1)) != CERR_None) return n;
return CheckUTF16(*n2);
}
KMX_DWORD BuildVKDictionary(PFILE_KEYBOARD fk)
{
KMX_DWORD i;
size_t len = 0;
if (fk->cxVKDictionary == 0) return CERR_None;
for (i = 0; i < fk->cxVKDictionary; i++)
{
len += u16len(fk->dpVKDictionary[i].szName) + 1;
}
PKMX_WCHAR storeval = new KMX_WCHAR[len], p = storeval;
for (i = 0; i < fk->cxVKDictionary; i++)
{
u16ncpy(p, fk->dpVKDictionary[i].szName, len - (size_t)(p - storeval)); // I3481
p = (PKMX_WCHAR) u16chr(p, 0);
*p = ' ';
p++;
}
p--;
*p = 0;
KMX_DWORD dwStoreID;
KMX_DWORD msg = AddStore(fk, TSS_VKDICTIONARY, storeval, &dwStoreID);
delete[] storeval;
return msg;
}
int GetVKCode(PFILE_KEYBOARD fk, PKMX_WCHAR p)
{
KMX_DWORD i;
for (i = 0; i < fk->cxVKDictionary; i++)
if (u16icmp(fk->dpVKDictionary[i].szName, p) == 0)
return i + VK__MAX + 1; // 256
if (fk->cxVKDictionary % 10 == 0)
{
PFILE_VKDICTIONARY pvk = new FILE_VKDICTIONARY[fk->cxVKDictionary + 10];
memcpy(pvk, fk->dpVKDictionary, fk->cxVKDictionary * sizeof(FILE_VKDICTIONARY));
delete fk->dpVKDictionary;
fk->dpVKDictionary = pvk;
}
u16ncpy(fk->dpVKDictionary[fk->cxVKDictionary].szName, p, _countof(fk->dpVKDictionary[fk->cxVKDictionary].szName) ); // I3481
fk->dpVKDictionary[fk->cxVKDictionary].szName[SZMAX_VKDICTIONARYNAME - 1] = 0;
fk->cxVKDictionary++;
return fk->cxVKDictionary + VK__MAX; // 256-1
}
int GetDeadKey(PFILE_KEYBOARD fk, PKMX_WCHAR p)
{
KMX_DWORD i;
for (i = 0; i < fk->cxDeadKeyArray; i++)
if (u16icmp(fk->dpDeadKeyArray[i].szName, p) == 0)
return i + 1;
if (fk->cxDeadKeyArray % 10 == 0)
{
PFILE_DEADKEY dk = new FILE_DEADKEY[fk->cxDeadKeyArray + 10];
memcpy(dk, fk->dpDeadKeyArray, fk->cxDeadKeyArray * sizeof(FILE_DEADKEY));
delete[] fk->dpDeadKeyArray;
fk->dpDeadKeyArray = dk;
}
u16ncpy(fk->dpDeadKeyArray[fk->cxDeadKeyArray].szName,p, _countof(fk->dpDeadKeyArray[fk->cxDeadKeyArray].szName)); // I3481
fk->dpDeadKeyArray[fk->cxDeadKeyArray].szName[SZMAX_DEADKEYNAME - 1] = 0;
fk->cxDeadKeyArray++;
return fk->cxDeadKeyArray;
}
void RecordDeadkeyNames(PFILE_KEYBOARD fk)
{
KMX_WCHAR buf[SZMAX_DEADKEYNAME + 16];
PKMX_WCHAR p_buf =buf;
KMX_DWORD i;
for (i = 0; i < fk->cxDeadKeyArray; i++)
{
u16sprintf(buf, _countof(buf), L"%ls%d %ls", u16fmt(DEBUGSTORE_DEADKEY).c_str(), (int)i, u16fmt(fk->dpDeadKeyArray[i].szName).c_str()); // I3481
AddDebugStore(fk, buf);
}
}
KMX_BOOL IsValidCallStore(PFILE_STORE fs)
{
int i;
PKMX_WCHAR p;
for (i = 0, p = fs->dpString; *p; p++)
if (*p == ':') i++;
else if (!((*p >= 'a' && *p <= 'z') ||
(*p >= 'A' && *p <= 'Z') ||
(*p >= '0' && *p <= '9') ||
*p == '.' ||
*p == '_'))
return FALSE;
return i == 1;
}
FILE* CreateTempFile()
{
//return tmpfile();
KMX_CHAR szTempPathBuffer[MAX_PATH], szTempFileName[MAX_PATH]; // I3228 // I3510
if (!tmpfile())
return NULL;
if (!tmpnam(szTempPathBuffer))
return NULL; // I3228 // I3510
return tmpfile();
}
///////////////////
FILE* UTF16TempFromUTF8(FILE* fp_in , KMX_BOOL hasPreamble)
{
FILE *fp_out = CreateTempFile();
if(fp_out == NULL) // I3228 // I3510
{
fclose(fp_in);
return NULL; //return INVALID_HANDLE_VALUE; can I exchange that?
}
PKMX_BYTE buf, p;
PKMX_WCHAR outbuf, poutbuf;
KMX_DWORD len;
DWORD len2;
KMX_WCHAR prolog = 0xFEFF;
fwrite(&prolog,2, 1, fp_out);
fseek(fp_in, 0, SEEK_END);
len = ftell(fp_in);
fseek(fp_in, 0, SEEK_SET);
if (hasPreamble) {
fseek( fp_in,3,SEEK_SET); // Cut off UTF-8 marker
len -= 3;
}
buf = new KMX_BYTE[len + 1]; // null terminated
outbuf = new KMX_WCHAR[len + 1];
len2= fread(buf,1,len,fp_in);
if (len2) {
buf[len2] = 0;
p = buf;
poutbuf = outbuf;
if (hasPreamble) {
// We have a preamble, so we attempt to read as UTF-8 and allow conversion errors to be filtered. This is not great for a
// compiler but matches existing behaviour -- in future versions we may not do lenient conversion.
ConversionResult cr = ConvertUTF8toUTF16(&p, &buf[len2], (UTF16 **)&poutbuf, (const UTF16 *)&outbuf[len], lenientConversion);
fwrite(outbuf, (KMX_DWORD)(INT_PTR)(poutbuf - outbuf) * 2 , 1, fp_out);
}
else {
// No preamble, so we attempt to read as strict UTF-8 and fall back to ANSI if that fails
ConversionResult cr = ConvertUTF8toUTF16(&p, &buf[len2], (UTF16 **)&poutbuf, (const UTF16 *)&outbuf[len], strictConversion);
if (cr == sourceIllegal) {
// Not a valid UTF-8 file, so fall back to ANSI
// AddCompileMessage(CHINT_NonUnicodeFile);
// note, while this message is defined, for now we will not emit it
// because we don't support HINT/INFO messages yet and we don't want
// this to cause a blocking compile at this stage
// do strtowstr only when no invalid characters are found
if( p==0){
poutbuf = strtowstr((PKMX_STR)buf);
fwrite(poutbuf, (KMX_DWORD)u16len(poutbuf) * 2 , 1, fp_out);
delete[] poutbuf;
}
}
else {
fwrite(outbuf, (KMX_DWORD)(INT_PTR)(poutbuf - outbuf) * 2 , 1, fp_out);
}
}
}
fclose( fp_in);
delete[] buf;
delete[] outbuf;
fseek( fp_out,2,SEEK_SET);
return fp_out;
}
extern "C" void __declspec(dllexport) Keyman_Diagnostic(int mode) {
if (mode == 0) {
RaiseException(0x0EA0BEEF, EXCEPTION_NONCONTINUABLE, 0, NULL);
}
}
PFILE_STORE FindSystemStore(PFILE_KEYBOARD fk, KMX_DWORD dwSystemID)
{
assert(fk != NULL);
assert(dwSystemID != 0);
PFILE_STORE sp = fk->dpStoreArray;
for (KMX_DWORD i = 0; i < fk->cxStoreArray; i++, sp++) {
if (sp->dwSystemID == dwSystemID) {
return sp;
}
}
return NULL;
}