/* 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 #include #include #include #include #include #include "cmdkeyb.h" #include 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,iSaveSize; CHAR szKeybCode[12]; CHAR szDir[MAX_PATH+15]; CHAR szBuf[28]; 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; #if defined(NEC_98) setDX(0); return; #endif // NEC_98 // 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. // iSaveSize = iSize = GetSystemDirectory(szDir, MAX_PATH); // can't get the system directory! if (!iSize || iSize > MAX_PATH) { goto NoInstallkb16; } // convert the system directory to short name cbData = GetShortPathName(szDir, 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, , 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: // <\0xd>, is the length of // 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 + 1]; // 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; }