|
|
/* cmdkeyb.c - Keyboard layout support routines
* * * Modification History: * * YST 14-Jan_1993 Created * * 08-Sept-1998, williamh, add third-party KDF support. */
#include "cmd.h"
#include <winconp.h>
#include <cmdsvc.h>
#include <softpc.h>
#include <mvdm.h>
#include <ctype.h>
#include <string.H>
#include "cmdkeyb.h"
#include <winnls.h>
#include "host_def.h"
CHAR szPrev[5] = "US"; INT iPrevCP = 437; CHAR szPrevKbdID[8] = "";
extern BOOL bPifFastPaste;
/************************************************************************\
* * FUNCTION: VOID cmdGetKbdLayout( VOID ) * * Input Client (DX) = 0 - Keyb.com not installed * 1 - Keyb.com installed * Client (DS:SI) = pointer where exe name has to be placed * Client (DS:CX) = pointer where command options are placed * * Output * Success (DX = 1 ) * Client (DS:SI) = Keyb.com execuatable string * Client (DS:CX) = command options * * Failure (DX = 0) * * COMMENTS: This function check KEYBOARD ID for Win session * and if ID != US then return lines with * filename and options to COMMAND.COM * * If bPifFastPaste is FALSE, then we always run kb16 * for all keyboard ID including US, to give us a more * bios compatible Int 9 handler. 10-Jun-1993 Jonle * * * HISTORY: 01/05/93 YSt Created. * \************************************************************************/
VOID cmdGetKbdLayout( VOID ) { INT iSize; CHAR szKeybCode[12]; CHAR szDir[MAX_PATH+15]; CHAR szBuf[5]; CHAR szNewKbdID[8]; CHAR szAutoLine[MAX_PATH+40]; CHAR szKDF[MAX_PATH]; PCHAR pVDMKeyb; INT iKeyb; HKEY hKey; HKEY hKeyLayout; DWORD dwType; DWORD retCode; INT iNewCP; DWORD cbData; WORD KeybID; OFSTRUCT ofstr; LANGID LcId = GetSystemDefaultLangID(); int keytype;
// Get information about 16 bit KEYB.COM from VDM
iKeyb = getDX();
// The whole logic here is to decide:
// (1). if we have to run kb16.com at all.
// (2). if we have to run kb16.com, what parameters we should pass along,
// such as keyboard id, language id, code page id and kdf file name.
// We do not load kb16.com at all if one of the following
// conditions is met:
// (1). We can not find the console keyboard layout id.
// (2). The console keyvoard layout id is US and kb16.com is not loaded
// and fast paste is disabled.
// (3). We can not get the dos keyboard id/dos key code.
// (4). The new (language id, keyboard id, code page id) is the same
// as the one we loaded previously.
// (5). we can not find kb16.com.
// (6). we can not find the kdf file that supports the
// (language id, keyboard id, code page id) combination.
//
// If everything goes as planned, we end up with a command
// contains kb16.com fully qualified name and a command line contains
// appropriate parameters to kb16.com
//
if (LcId == MAKELANGID(LANG_JAPANESE,SUBLANG_DEFAULT)) { // JAPAN build. Language id is always "JP" and code page is either 932
// or 437 depends on keyboard type.
iNewCP = 437; if (7 == GetKeyboardType(0)) { keytype = GetKeyboardType(1); if (keytype == 1 || keytype == 2 || keytype == 3 || (keytype & 0xff00) == 0x1200) iNewCP = 932; } szBuf[0] = 'J'; szBuf[1] = 'P'; szBuf[2] = '\0'; // no keyboard id available.
szNewKbdID[0] = '\0'; } else { //
// check point #1: see if we can get the console keyboard layout id
//
if (!GetConsoleKeyboardLayoutName(szKeybCode)) goto NoInstallkb16; //
// check point #2: see if the layout is US and kb16.com is loaded and
// fast paste is disabled.
// If kb16.com is loaded, we need to run it again
// so that it will load the correct layout.
// If fast paste is disable, we load kb16.com which
// definitely will slow down keys delivery.
//
if( bPifFastPaste && !strcmp(szKeybCode, US_CODE) && !iKeyb) goto NoInstallkb16; //
// check point #3: see if we can get the language id and keyboard id(if any)
//
// OPEN THE KEY.
sprintf(szAutoLine, "%s%s", KBDLAYOUT_PATH, DOSCODES_PATH); if (ERROR_SUCCESS != RegOpenKeyEx (HKEY_LOCAL_MACHINE, // Key handle at root level.
szAutoLine, // Path name of child key.
0, // Reserved.
KEY_EXECUTE, // Requesting read access.
&hKey)) // Address of key to be returned.
goto NoInstallkb16;
cbData = sizeof(szBuf); // Query for line from REGISTER file
retCode = RegQueryValueEx(hKey, szKeybCode, NULL, &dwType, szBuf, &cbData);
RegCloseKey(hKey); if (ERROR_SUCCESS != retCode || REG_SZ != dwType || !cbData) goto NoInstallkb16; //
// szBuf now contains language id('SP' for spanish, for example).
//
// look for keyboard id number. For Daytona, Turkish and Italian both
// have one key code and two layouts.
szNewKbdID[0] = '\0'; cbData = sizeof(szNewKbdID); sprintf(szAutoLine, "%s%s", KBDLAYOUT_PATH, DOSIDS_PATH); if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, szAutoLine, 0, KEY_EXECUTE, &hKey ) == ERROR_SUCCESS) { retCode = RegQueryValueEx(hKey, szKeybCode, NULL, &dwType, szNewKbdID, &cbData); if (ERROR_SUCCESS != retCode || REG_SZ != dwType || !cbData) szNewKbdID[0] = '\0';
RegCloseKey(hKey); }
iNewCP = GetConsoleCP();
}
//
// check point #4: see if there are any changes in ids
//
// see if there are changes in language id, keyboard id and code page id.
if(bPifFastPaste && iNewCP == iPrevCP && !_stricmp(szBuf, szPrev) && !_stricmp(szNewKbdID, szPrevKbdID)) { goto NoInstallkb16; }
//
// Check point #5: see if kb16.com can be found.
//
// kb16.com should be found in GetSystemDirectory()\system32 subdirectory.
//
// convert the system directory to short name
cbData = GetShortPathName(pszSystem32Path, szDir, MAX_PATH); if (!cbData || cbData >= MAX_PATH) goto NoInstallkb16;
sprintf(szAutoLine, "%s%s", szDir, // System directory
KEYB_COM // keyb.com
); // if the fully qualified path name to kb16.com is too long
// we must fail because Dos can not swallow a long path name.
if (strlen(szAutoLine) > 128) goto NoInstallkb16;
dwType = GetFileAttributes(szAutoLine); if (dwType == 0xFFFFFFFF || (dwType & FILE_ATTRIBUTE_DIRECTORY) != 0) { goto NoInstallkb16; }
//
// Check point #6: see if we can find kdf file that support the
// (language id, keyboard id, code page id) combination
//
//
// first, convert keyboard id from string to binary if we have one.
//
KeybID = (szNewKbdID[0]) ? (WORD)strtoul(szNewKbdID, NULL, 10) : 0; cbData = sizeof(szKDF) / sizeof(CHAR);
// locate the kdf file.
if (!LocateKDF(szBuf, KeybID, (WORD)iNewCP, szKDF, &cbData)) { goto NoInstallkb16; } // convert the kdf name to short name
cbData = GetShortPathName(szKDF, szKDF, sizeof(szKDF)/ sizeof(CHAR)); if (!cbData || cbData >= sizeof(szKDF) / sizeof(CHAR)) { goto NoInstallkb16; } //
// everything is checked and in place. Now compose the command
// line to execute kb16.com
// first, the command
pVDMKeyb = (PCHAR) GetVDMAddr((USHORT) getDS(), (USHORT) getSI()); strcpy(pVDMKeyb, szAutoLine); // then the parameters
// The format is: XX,YYY, <kdf file>, where XXX is the language id
// and YYY is the code page id.
pVDMKeyb = (PCHAR) GetVDMAddr((USHORT) getDS(), (USHORT) getCX()); // The first byte is resevered for the length of the string.
sprintf(szAutoLine, " %s,%d,%s", szBuf, // keyboard code
iNewCP, // new code page
szKDF // keyboard.sys
); // if we have a keyboard id, pass it also
if (szNewKbdID[0]) { strcat(szAutoLine, " /id:"); strcat(szAutoLine, szNewKbdID); } // standard parameter line has the format:
// <length><line text><\0xd>, <length> is the length of <line text>
//
iSize = strlen(szAutoLine); szAutoLine[iSize] = 0xd; // Move the line to 16bits, including the terminated cr char
RtlMoveMemory(pVDMKeyb + 1, szAutoLine, iSize + 1); *pVDMKeyb = (CHAR)iSize; // Save new layout ID and code page for next call
strcpy(szPrev, szBuf); strcpy(szPrevKbdID, szNewKbdID); iPrevCP = iNewCP;
setDX(1); return;
NoInstallkb16: setDX(0); cmdInitConsole(); // make sure conoutput is on
return; }
//
// This function locates the appropriate keyboard definition file from
// the given language, keyboard and code page id. It searches the registry
// for third-party installed KDF files first and then falls back to
// the system default, %systemroot%\system32\keyboard.sys.
//
// INPUT:
// LanguageID -- the language id
// KeyboardID -- the optional keyboard id, 0 means do not care
// CodePageID -- the code page id
// Buffer -- the buffer to receive fully qualified kdf file name
// BufferSize -- the size of Buffer in bytes
//
// OUTPUT:
// TRUE -- Buffer is filled with the kdf fully qualified file name and
// *BufferSize is set with the size of the file name, not including
// the null terminated char. If no kdf file can be found,
// the Buffer is terminated with NULL and *BufferSize if set to 0.
// FALSE -- error. GetLastError() should return the error code
// If the error occurs because the provided buffer is too small
// *BufferSize will set to the required size(excluding null
// terminated char) and error code will be set to
// ERROR_INSUFFICIENT_BUFFER
//
BOOL LocateKDF( CHAR* LanguageID, WORD KeyboardID, WORD CodePageID, LPSTR Buffer, DWORD* BufferSize ) { HKEY hKeyWow; BOOL Result; DWORD dw, Type; DWORD Attributes; DWORD ErrorCode; CHAR* KDFName; CHAR* LocalBuffer; CHAR* FinalKDFName; CHAR FullName[MAX_PATH*2];
// validate buffer parameter first
if (!CodePageID || !LanguageID || !BufferSize || (*BufferSize && !Buffer)) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } // Open the registry to see if we have alternative kdf files avaialble.
// We seach the file from atlternative file list in the registry first.
// The first KDF in the list has the highest rank and the last one has
// the lowest. The search starts from highest rank and then goes
// on toward the lower ones. As soon as a KDF file is found, the search
// stops. If no appropriate KDF can be found in the alternative list,
// the default kdf, keyboard.sys, will be used.
//
// FinalKDFName serves as an indicator. If it is NULL,
// we do not find any file that satisfies the request.
FinalKDFName = NULL; LocalBuffer = NULL; if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_STR_WOW, 0, KEY_EXECUTE, &hKeyWow ) == ERROR_SUCCESS) { // first probe for size
dw = 0; RegQueryValueEx(hKeyWow, REG_STR_ALTKDF_FILES, NULL, &Type, NULL, &dw); if (dw && (REG_MULTI_SZ == Type)) { // we have something in the registry. Allocate a buffer to reteive
// it. We want the value to be double null terminated,
// so we add one more char in case it is a REG_SZ.
// The returned size from RegQueryValueEx includes the
// null terminated char(and the double null chars for
// REG_MULTI_SZ. By adding one more char, we are in
// good shape.
ASSERT(!LocalBuffer); LocalBuffer = malloc((dw + 1)* sizeof(CHAR)); if (LocalBuffer) { LocalBuffer[0] = '\0'; if (RegQueryValueEx(hKeyWow, REG_STR_ALTKDF_FILES, NULL, &Type, LocalBuffer, &dw) == ERROR_SUCCESS && dw) { KDFName = LocalBuffer; while ('\0' != *KDFName) { // See if we can find the file first.
Attributes = GetFileAttributesA(KDFName); if (0xFFFFFFFF == Attributes) { // file not found, do a search
if (SearchPathA(NULL, // no path
KDFName, NULL, // no extension
sizeof(FullName) / sizeof(CHAR), FullName, NULL )) { FinalKDFName = FullName; } } else { FinalKDFName = KDFName; } if (MatchKDF(LanguageID, KeyboardID, CodePageID, FinalKDFName)) break; KDFName += strlen(KDFName) + 1; FinalKDFName = NULL; } } } else { // not enough memory
RcMessageBox(EG_MALLOC_FAILURE, NULL, NULL, RMB_ICON_BANG | RMB_ABORT); TerminateVDM(); } } if (!FinalKDFName) { // either no alternative kdf files are specified in the registry
// or none of them contains the required specification,
// use the default kdf file
FullName[0] = '\0'; GetSystemDirectory(FullName, sizeof(FullName) / sizeof(CHAR));
if (!_stricmp(LanguageID, "JP") && 7 == GetKeyboardType(0) ) { // For Japanese language ID, different keyboard types have different
// default kdf.
int Keytype;
Keytype = GetKeyboardType(1); if (Keytype == 1) strcat(FullName, KDF_AX); else if (Keytype == 2) strcat(FullName, KDF_106); else if (Keytype == 3) strcat(FullName, KDF_IBM5576_02_03); else if ((Keytype & 0xFF00) == 0x1200) strcat(FullName, KDF_TOSHIBA_J3100); else strcat(FullName, KEYBOARD_SYS); } else strcat(FullName, KEYBOARD_SYS);
FinalKDFName = FullName; } RegCloseKey(hKeyWow); } if (FinalKDFName) { dw = strlen(FinalKDFName); if (dw && dw < *BufferSize) { strcpy(Buffer, FinalKDFName); *BufferSize = dw; Result = TRUE; } else { *BufferSize = dw; SetLastError(ERROR_INSUFFICIENT_BUFFER); Result = FALSE; } } else { Result = FALSE; *BufferSize = 0; SetLastError(ERROR_FILE_NOT_FOUND); } //
// finally, free the buffer we allocated
//
if (LocalBuffer) free(LocalBuffer); return Result; }
//
// This function determines if the given kdf supports the given
// (language id, keyboard id, code page id) combination
//
// INPUT:
// LanguageID -- the language.
// KeyboardID -- optional keyboard id. 0 if do not care
// CodePageID -- code page id
// KDFPath -- fully qualified kdf file
// OUTPUT:
// TRUE -- The kdf contains the given combination
// FALSE -- either the kdf does not contain the combination or
// can not determine.
//
BOOL MatchKDF( CHAR* LanguageID, WORD KeyboardID, WORD CodePageID, LPCSTR KDFPath ) { HANDLE hKDF; KDF_HEADER Header; KDF_LANGID_ENTRY LangIdEntry; DWORD BytesRead, BufferSize; WORD Index; DWORD LangIdEntryOffset; PKDF_CODEPAGEID_OFFSET pCodePageIdOffset; BOOL Matched;
if (!KDFPath || !LanguageID || !CodePageID) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } Matched = FALSE;
LangIdEntryOffset = 0; // open the kdf file.
hKDF = CreateFile(KDFPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (INVALID_HANDLE_VALUE != hKDF && ReadFile(hKDF, &Header, sizeof(Header),&BytesRead, NULL) && BytesRead == sizeof(Header) && Header.TotalLangIDs && Header.TotalKeybIDs && !strncmp(Header.Signature, KDF_SIGNATURE, sizeof(Header.Signature)) ) { // The file header is loaded, the signature checked and sanity check
// on language and keyboard id counts is also done.
// We are now ready to verfiy if the given language id, keyboard id
// and code page id is supported in this file.
// A KDF has two sets of offset table. One is based on language ID
// while the other one is based on keyboard id. Since a language ID
// may contain multiple keyboard id, the keyboard id set is always
// encompass the language id table.
// If the caller gives us a keyboard id, we use the id as the
// key for search and verify language id when we found the keyboard
// id. If no keyboard id is provided, we use the language id as the
// key.
if (KeyboardID) { // move the file pointer to the keyboard id offset array
BufferSize = sizeof(KDF_LANGID_OFFSET) * Header.TotalLangIDs; BufferSize = SetFilePointer(hKDF, BufferSize, NULL, FILE_CURRENT); if (0xFFFFFFFF != BufferSize) { PKDF_KEYBOARDID_OFFSET pKeybIdOffset; BufferSize = sizeof(KDF_KEYBOARDID_OFFSET) * Header.TotalKeybIDs; pKeybIdOffset = (PKDF_KEYBOARDID_OFFSET)malloc(BufferSize); if (!pKeybIdOffset) { // not enough memory
RcMessageBox(EG_MALLOC_FAILURE, NULL, NULL, RMB_ICON_BANG | RMB_ABORT); TerminateVDM(); } if (ReadFile(hKDF, pKeybIdOffset, BufferSize, &BytesRead, NULL) && BytesRead == BufferSize) { // loop though each KDF_KEYBOARDID_OFFSET to see
// if the keyboard id matches.
for (Index = 0; Index < Header.TotalKeybIDs; Index++) { if (pKeybIdOffset[Index].ID == KeyboardID) { // got it. Remeber the file offset to
// the KDF_LANGID_ENTRY
LangIdEntryOffset = pKeybIdOffset[Index].DataOffset; break; } } } free(pKeybIdOffset); } } else { PKDF_LANGID_OFFSET pLangIdOffset; BufferSize = sizeof(KDF_LANGID_OFFSET) * Header.TotalLangIDs; pLangIdOffset = (PKDF_LANGID_OFFSET)malloc(BufferSize); if (!pLangIdOffset) { // not enough memory
RcMessageBox(EG_MALLOC_FAILURE, NULL, NULL, RMB_ICON_BANG | RMB_ABORT); TerminateVDM(); } if (ReadFile(hKDF, pLangIdOffset, BufferSize, &BytesRead, NULL) && BytesRead == BufferSize) { // loop through each KDF_LANGID_OFFSET to see if
// language id matches
for (Index = 0; Index < Header.TotalLangIDs; Index++) { if (IS_LANGID_EQUAL(pLangIdOffset[Index].ID, LanguageID)) { LangIdEntryOffset = pLangIdOffset[Index].DataOffset; break; } } } free(pLangIdOffset); } if (LangIdEntryOffset) { BufferSize = SetFilePointer(hKDF, LangIdEntryOffset, NULL, FILE_BEGIN); if (0xFFFFFFFF != BufferSize && ReadFile(hKDF, &LangIdEntry, sizeof(LangIdEntry), &BytesRead, NULL) && BytesRead == sizeof(LangIdEntry)) { // sanity checks
if (IS_LANGID_EQUAL(LangIdEntry.ID, LanguageID) && LangIdEntry.TotalCodePageIDs) { // the KDF_LANGID_ENTRY looks fine. Now retrieve
// its code page offset table and search the given
// code page id
BufferSize = LangIdEntry.TotalCodePageIDs * sizeof(KDF_CODEPAGEID_OFFSET); pCodePageIdOffset = (PKDF_CODEPAGEID_OFFSET)malloc(BufferSize); if (!pCodePageIdOffset) { // not enough memory
RcMessageBox(EG_MALLOC_FAILURE, NULL, NULL, RMB_ICON_BANG | RMB_ABORT); TerminateVDM(); } if (ReadFile(hKDF, pCodePageIdOffset, BufferSize, &BytesRead, NULL) && BytesRead == BufferSize) { for (Index = 0; Index < LangIdEntry.TotalCodePageIDs; Index++) { if (CodePageID == pCodePageIdOffset[Index].ID) { Matched = TRUE; break; } } } free(pCodePageIdOffset); } } } CloseHandle(hKDF); } return Matched; }
|