mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-21 15:57:42 +00:00
Fixes #4423. Using a named character constant with a store name that included characters outside ascii could cause kmcmpdll to crash with an assertion failure. This fix allows store names defined in the .kmn file to use characters outside ascii. Note that more work needs to be done on which characters are acceptable to use, as this is somewhat implementation-specific according to the C++ specification for `iswalpha`. Added a test case to verify various named code constant examples. Also removes dead code for `IsCJKUnifiedIdeograph` as this was never used and somewhat pointless in any case. NamedCodeConstants.cpp could stand to be rewritten using `std::map` or equivalent. Current implementation is pretty icky.
323 lines
8.3 KiB
C++
323 lines
8.3 KiB
C++
/*
|
|
Name: NamedCodeConstants
|
|
Copyright: Copyright (C) 2003-2017 SIL International.
|
|
Documentation:
|
|
Description:
|
|
Create Date: 19 Jul 2011
|
|
|
|
Modified Date: 13 Dec 2012
|
|
Authors: mcdurdin
|
|
Related Files:
|
|
Dependencies:
|
|
|
|
Bugs:
|
|
Todo:
|
|
Notes:
|
|
History: 19 Jul 2011 - mcdurdin - I2993 - Named code constants cause a warning 0x208D to appear
|
|
24 Oct 2012 - mcdurdin - I3481 - V9.0 - Eliminate unsafe calls in C++
|
|
06 Feb 2012 - mcdurdin - I3056 - If file was not found in first search folder, named code constants failed to compile
|
|
06 Feb 2012 - mcdurdin - I3056 - UTF-8 support for named code constants file
|
|
03 Nov 2012 - mcdurdin - I3512 - V9.0 - Merge of I3056 - If file was not found in first search folder, named code constants failed to compile
|
|
13 Dec 2012 - mcdurdin - I3641 - V9.0 - compiler dll buffer overrun bugs
|
|
*/
|
|
|
|
#include "pch.h"
|
|
#include <io.h>
|
|
#include <limits.h>
|
|
#include "NamedCodeConstants.h"
|
|
#include "kmcmpdll.h"
|
|
|
|
extern char CompileDir[];
|
|
|
|
|
|
int IsHangulSyllable(const wchar_t *codename, int *code);
|
|
|
|
BOOL FileExists(const char *filename)
|
|
{
|
|
_finddata_t fi;
|
|
intptr_t n;
|
|
|
|
if((n = _findfirst(filename, &fi)) != -1) // I3056 // I3512
|
|
{
|
|
_findclose(n);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
NamedCodeConstants::NamedCodeConstants()
|
|
{
|
|
nEntries = 0;
|
|
entries = NULL;
|
|
nEntries_file = 0;
|
|
entries_file = NULL;
|
|
reindex();
|
|
}
|
|
|
|
NamedCodeConstants::~NamedCodeConstants()
|
|
{
|
|
if(entries) delete entries;
|
|
if(entries_file) delete entries_file;
|
|
}
|
|
|
|
void NamedCodeConstants::AddCode(int n, const wchar_t *p, DWORD storeIndex)
|
|
{
|
|
if((nEntries_file % ALLOC_SIZE) == 0)
|
|
{
|
|
NCCENTRY *bn = new NCCENTRY[nEntries_file + ALLOC_SIZE];
|
|
if(nEntries_file > 0)
|
|
{
|
|
memcpy(bn, entries_file, sizeof(NCCENTRY) * nEntries_file);
|
|
delete entries_file;
|
|
}
|
|
entries_file = bn;
|
|
}
|
|
|
|
entries_file[nEntries_file].code = n;
|
|
wcsncpy_s(entries_file[nEntries_file].name, _countof(entries_file[nEntries_file].name), p, MAX_ENAME); // I3481
|
|
entries_file[nEntries_file].name[MAX_ENAME] = 0;
|
|
|
|
for (wchar_t *r = entries_file[nEntries_file].name; *r; r++)
|
|
if (!iswalnum(*r) && *r != '-') *r = '_';
|
|
|
|
entries_file[nEntries_file].storeIndex = storeIndex;
|
|
nEntries_file++;
|
|
}
|
|
|
|
void NamedCodeConstants::AddCode_IncludedCodes(int n, const wchar_t *p)
|
|
{
|
|
if((nEntries % ALLOC_SIZE) == 0)
|
|
{
|
|
NCCENTRY *bn = new NCCENTRY[nEntries + ALLOC_SIZE];
|
|
if(nEntries > 0)
|
|
{
|
|
memcpy(bn, entries, sizeof(NCCENTRY) * nEntries);
|
|
delete entries;
|
|
}
|
|
entries = bn;
|
|
}
|
|
|
|
entries[nEntries].code = n;
|
|
wcsncpy_s(entries[nEntries].name, _countof(entries[nEntries].name), p, MAX_ENAME); // I3481
|
|
entries[nEntries].name[MAX_ENAME] = 0;
|
|
for (wchar_t *r = entries[nEntries].name; *r; r++)
|
|
if (!iswalnum(*r) && *r != '-') *r = '_';
|
|
|
|
entries[nEntries].storeIndex = 0xFFFFFFFFL;
|
|
nEntries++;
|
|
}
|
|
|
|
int __cdecl sort_entries(const void *elem1, const void *elem2)
|
|
{
|
|
return _wcsicmp(
|
|
((NCCENTRY *)elem1)->name,
|
|
((NCCENTRY *)elem2)->name);
|
|
}
|
|
|
|
BOOL NamedCodeConstants::IntLoadFile(const char *filename)
|
|
{
|
|
FILE *fp = NULL;
|
|
if(fopen_s(&fp, filename, "rt") != 0) return FALSE; // I3481
|
|
|
|
char str[256], *p, *q, *context = NULL;
|
|
BOOL neol, first = TRUE;
|
|
|
|
while(fgets(str, 256, fp))
|
|
{
|
|
neol = *(strchr(str, 0) - 1) == '\n';
|
|
p = strtok_s(str, ";", &context); // I3481
|
|
q = strtok_s(NULL, ";\n", &context);
|
|
if(p && q)
|
|
{
|
|
if(first && *p == (char)0xEF && *(p+1) == (char)0xBB && *(p+2) == (char)0xBF) p += 3; // I3056 UTF-8 // I3512
|
|
first = FALSE;
|
|
_strupr_s(q, strlen(q)+1); // I3481 // I3641
|
|
int n = strtol(p, NULL, 16);
|
|
if (*q != '<') {
|
|
PWSTR q0 = strtowstr(q);
|
|
AddCode_IncludedCodes(n, q0);
|
|
delete[] q0;
|
|
}
|
|
}
|
|
if(!neol)
|
|
{
|
|
while(fgets(str, 256, fp)) if(*(strchr(str, 0)-1) == '\n') break;
|
|
}
|
|
}
|
|
|
|
fclose(fp);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL NamedCodeConstants::LoadFile(const char *filename)
|
|
{
|
|
char buf[260];
|
|
// Look in current directory first
|
|
strncpy_s(buf, _countof(buf), filename, 259); buf[259] = 0; // I3481
|
|
if(FileExists(buf))
|
|
return IntLoadFile(buf);
|
|
// Then look in keyboard file directory (CompileDir)
|
|
strncpy_s(buf, _countof(buf), CompileDir, 259); buf[259] = 0; // I3481
|
|
strncat_s(buf, _countof(buf), filename, 259-strlen(CompileDir)); buf[259] = 0;
|
|
if(FileExists(buf))
|
|
return IntLoadFile(buf);
|
|
// Finally look in kmcmpdll.dll directory
|
|
GetModuleFileName(0, buf, 260);
|
|
char *p = strrchr(buf, '\\'); if(p) p++; else p = buf;
|
|
*p = 0;
|
|
strncat_s(buf, _countof(buf), filename, 259-strlen(buf)); buf[259] = 0; // I3481 // I3641
|
|
if(FileExists(buf))
|
|
return IntLoadFile(buf);
|
|
|
|
reindex();
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void NamedCodeConstants::reindex()
|
|
{
|
|
if (entries != NULL) {
|
|
qsort(entries, nEntries, sizeof(NCCENTRY), sort_entries);
|
|
}
|
|
|
|
wchar_t c = L'.', d;
|
|
int i;
|
|
|
|
for(i = 0; i < 128; i++) chrindexes[i] = -1;
|
|
|
|
if (entries != NULL) {
|
|
for (i = 0; i < nEntries; i++)
|
|
{
|
|
d = towupper(entries[i].name[0]);
|
|
if (d != c && d >= 32 && d <= 127)
|
|
chrindexes[c = d] = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
int NamedCodeConstants::GetCode(const wchar_t *codename, DWORD *storeIndex)
|
|
{
|
|
*storeIndex = 0xFFFFFFFFL; // I2993
|
|
int code = GetCode_IncludedCodes(codename);
|
|
if(code) return code;
|
|
for(int i = 0; i < nEntries_file; i++)
|
|
if(!_wcsicmp(entries_file[i].name, codename))
|
|
{
|
|
*storeIndex = entries_file[i].storeIndex;
|
|
return entries_file[i].code;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int NamedCodeConstants::GetCode_IncludedCodes(const wchar_t *codename)
|
|
{
|
|
wchar_t c = towupper(*codename);
|
|
int code;
|
|
|
|
if(IsHangulSyllable(codename, &code)) return code;
|
|
|
|
if(c < 32 || c > 127 || chrindexes[c] < 0) return 0;
|
|
for(int n = chrindexes[c]; n < nEntries && towupper(entries[n].name[0]) == c; n++)
|
|
{
|
|
int cmp = _wcsicmp(codename, entries[n].name);
|
|
if(cmp == 0) return entries[n].code;
|
|
if(cmp < 0) break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
|
|
Hangul Syllables
|
|
|
|
*/
|
|
|
|
const int
|
|
HangulSBase = 0xAC00,
|
|
HangulLBase = 0x1100,
|
|
HangulVBase = 0x1161,
|
|
HangulTBase = 0x11A7,
|
|
HangulLCount = 19,
|
|
HangulVCount = 21,
|
|
HangulTCount = 28,
|
|
HangulNCount = HangulVCount * HangulTCount, // 588
|
|
HangulSCount = HangulLCount * HangulNCount; // 11172
|
|
|
|
const wchar_t *
|
|
Hangul_JAMO_L_TABLE[] = {
|
|
L"G", L"GG", L"N", L"D", L"DD", L"R", L"M", L"B", L"BB",
|
|
L"S", L"SS", L"", L"J", L"JJ", L"C", L"K", L"T", L"P", L"H" };
|
|
|
|
const wchar_t *
|
|
Hangul_JAMO_V_TABLE[] = {
|
|
L"A", L"AE", L"YA", L"YAE", L"EO", L"E", L"YEO", L"YE", L"O",
|
|
L"WA", L"WAE", L"OE", L"YO", L"U", L"WEO", L"WE", L"WI",
|
|
L"YU", L"EU", L"YI", L"I" };
|
|
|
|
const wchar_t *
|
|
Hangul_JAMO_T_TABLE[] = {
|
|
L"", L"G", L"GG", L"GS", L"N", L"NJ", L"NH", L"D", L"L", L"LG", L"LM",
|
|
L"LB", L"LS", L"LT", L"LP", L"LH", L"M", L"B", L"BS",
|
|
L"S", L"SS", L"NG", L"J", L"C", L"K", L"T", L"P", L"H" };
|
|
|
|
int IsHangulSyllable(const wchar_t *codename, int *code)
|
|
{
|
|
if(_wcsnicmp(codename, L"HANGUL_SYLLABLE_", 16)) return 0;
|
|
codename += 16;
|
|
if(!*codename) return 0;
|
|
|
|
int i, LIndex, VIndex, TIndex;
|
|
|
|
/* Find initial */
|
|
|
|
int ch = towupper(*codename);
|
|
if(strchr("GNDRMBSJCKTPH", ch))
|
|
{
|
|
/* Has an initial syllable */
|
|
int fdouble = towupper(*(codename+1)) == ch;
|
|
|
|
LIndex = -1;
|
|
for(i = 0; i < HangulLCount; i++)
|
|
if(Hangul_JAMO_L_TABLE[i][0] == ch &&
|
|
(!fdouble || (Hangul_JAMO_L_TABLE[i][1] == ch && fdouble)))
|
|
{
|
|
LIndex = i;
|
|
break;
|
|
}
|
|
if(LIndex == -1) return 0;
|
|
codename++;
|
|
if(fdouble) codename++;
|
|
}
|
|
else LIndex = 11; /* no initial */
|
|
|
|
/* Find vowel */
|
|
|
|
wchar_t V[4] = L"";
|
|
V[0] = *codename;
|
|
if(V[0] && strchr("AEIOUWY", towupper(*(codename+1)))) V[1] = *(codename+1);
|
|
if(V[1] && strchr("AEIOUWY", towupper(*(codename+2)))) V[2] = *(codename+2);
|
|
|
|
VIndex = -1;
|
|
for(i = 0; i < HangulVCount; i++)
|
|
if(!_wcsicmp(Hangul_JAMO_V_TABLE[i], V)) { VIndex = i; break; }
|
|
|
|
if(VIndex == -1) return 0;
|
|
|
|
codename += wcslen(V);
|
|
|
|
/* Find final */
|
|
|
|
TIndex = -1;
|
|
|
|
for(i = 0; i < HangulTCount; i++)
|
|
if(!_wcsicmp(Hangul_JAMO_T_TABLE[i], codename)) { TIndex = i; break; }
|
|
|
|
if(TIndex == -1) return 0;
|
|
|
|
/* Composition */
|
|
|
|
*code = (HangulSBase + (LIndex * HangulVCount + VIndex) * HangulTCount) + TIndex;
|
|
|
|
return 1;
|
|
}
|