/*++ Copyright (c) 1991-2000, Microsoft Corporation All rights reserved. Module Name: geo.c Abstract: This file contains the system APIs that provide geographical information. Private APIs found in this file: GetIS0639 GetGeoLCID External Routines found in this file: GetGeoInfoW GetUserGeoID SetUserGeoID EnumSystemGeoID Revision History: 11-20-99 WeiWu Created 03-07-00 lguindon Began Geo API port --*/ // // Include Files. // #include "nls.h" #include "nlssafe.h" // // Global Variables. // PGEOTABLEHDR gpGeoTableHdr = NULL; PGEOINFO gpGeoInfo = NULL; PGEOLCID gpGeoLCID = NULL; //////////////////////////////////////////////////////////////////////////// // // GetGeoLCID // // Returns the Locale ID associated with the Language Identifier and // Geographical Identifier. This routine scans the mapping table for the // corresponding combination. If nothing is found, the function returns // 0 as the Locale Identifier. // //////////////////////////////////////////////////////////////////////////// LCID GetGeoLCID( GEOID GeoId, LANGID LangId) { int ctr1, ctr2; if (pTblPtrs->pGeoInfo == NULL) { if (GetGeoFileInfo()) { return (0); } } // // Search for GEOID. // // Note: We can have more then one Language ID for one GEOID. // for (ctr1 = 0; ctr1 < pTblPtrs->nGeoLCID; ctr1++) { if (GeoId == pTblPtrs->pGeoLCID[ctr1].GeoId) { // // Search for Language ID // for (ctr2 = ctr1; ctr2 < pTblPtrs->nGeoLCID && pTblPtrs->pGeoLCID[ctr2].GeoId == GeoId; ctr2++) { if (pTblPtrs->pGeoLCID[ctr2].LangId == LangId) { return (pTblPtrs->pGeoLCID[ctr2].lcid); } } break; } } // // Nothing found, return zero // return ((LCID)0); } //-------------------------------------------------------------------------// // EXTERNAL API ROUTINES // //-------------------------------------------------------------------------// //////////////////////////////////////////////////////////////////////////// // // GetGeoInfoW // // Retrieves information about a geographical location on earth. The // required size is the number of characters. If cchData is zero, the // function returns the number of characters needed to copy to caller's // buffer. Otherwise, the function returns the number of characters copied // to caller's buffer if caller provided proper lpGeoData and cchData. // The function returns zero in the case of failure. // //////////////////////////////////////////////////////////////////////////// int WINAPI GetGeoInfoW( GEOID GeoId, DWORD GeoType, LPWSTR lpGeoData, int cchData, LANGID LangId) { int ctr1, ctr2, ctr3; int Length = 0; LPWSTR pString = NULL; WCHAR pTemp[MAX_REG_VAL_SIZE] = {0}; LCID Locale; LANGID DefaultLangId; PLOC_HASH pHashN; // // Invalid Parameter Check: // - count is negative // - NULL data pointer AND count is not zero // - invalid lang id // // NOTE: Invalid geo id is checked in the binary search below. // Invalid type is checked in the switch statement below. // Locale = MAKELCID(LangId, SORT_DEFAULT); VALIDATE_LOCALE(Locale, pHashN, FALSE); if ((cchData < 0) || ((lpGeoData == NULL) && (cchData > 0)) || (pHashN == NULL)) { SetLastError(ERROR_INVALID_PARAMETER); return (0); } // // Check if the section is mapped into memory. // if (pTblPtrs->pGeoInfo == NULL) { if (GetGeoFileInfo()) { return (0); } } // // Check if we are dealing with an invalid geoid. // if (GeoId == GEOID_NOT_AVAILABLE) { return (0); } // // Initialize variables for the binary search. // ctr1 = 0; ctr2 = pTblPtrs->nGeoInfo - 1; ctr3 = ctr2 >> 1; // // Binary search GEO data. // while (ctr1 <= ctr2) { if (GeoId == pTblPtrs->pGeoInfo[ctr3].GeoId) { // // Jump out of the loop. // break; } else { if (GeoId < pTblPtrs->pGeoInfo[ctr3].GeoId) { ctr2 = ctr3 - 1; } else { ctr1 = ctr3 + 1; } ctr3 = (ctr1 + ctr2) >> 1; } } // // See if we have found the requested element. // if (ctr1 > ctr2) { // // Could not find the Geo ID. // SetLastError(ERROR_INVALID_PARAMETER); return (0); } // // Get the appropriate information based on the requested GeoType. // switch (GeoType) { case ( GEO_NATION ) : { if (pTblPtrs->pGeoInfo[ctr3].GeoClass == GEOCLASS_NATION) { NlsConvertIntegerToString( (UINT)(pTblPtrs->pGeoInfo[ctr3].GeoId), 10, 0, pTemp, MAX_REG_VAL_SIZE ); pString = pTemp; } break; } case ( GEO_LATITUDE ) : { pString = pTblPtrs->pGeoInfo[ctr3].szLatitude; break; } case ( GEO_LONGITUDE ) : { pString = pTblPtrs->pGeoInfo[ctr3].szLongitude; break; } case ( GEO_ISO2 ) : { pString = pTblPtrs->pGeoInfo[ctr3].szISO3166Abbrev2; break; } case ( GEO_ISO3 ) : { pString = pTblPtrs->pGeoInfo[ctr3].szISO3166Abbrev3; break; } case ( GEO_RFC1766 ) : { // // Check if it's a valid LANGID. If not, get the default. // if (LangId == 0) { LangId = GetUserDefaultLangID(); } // // Make the corresponding LCID. // Locale = MAKELCID(LangId, SORT_DEFAULT); // // Get IS0639 value associated with the LANGID. // if (!GetLocaleInfoW( Locale, LOCALE_SISO639LANGNAME, pTemp, MAX_REG_VAL_SIZE )) { // // Try the Primary Language Identifier. // DefaultLangId = MAKELANGID(PRIMARYLANGID(LangId), SUBLANG_DEFAULT); if (DefaultLangId != LangId) { Locale = MAKELCID(DefaultLangId, SORT_DEFAULT); GetLocaleInfoW( Locale, LOCALE_SISO639LANGNAME, pTemp, MAX_REG_VAL_SIZE ); } } if (pTemp[0] != 0) { // // Construct the name to fit the form xx-yy where // xx is ISO639_1 name associated with the LANGID // and yy is the ISO3166 name 2 char abreviation. // if( FAILED(StringCchCatW(pTemp, ARRAYSIZE(pTemp), L"-")) || FAILED(StringCchCatW(pTemp, ARRAYSIZE(pTemp), pTblPtrs->pGeoInfo[ctr3].szISO3166Abbrev2))) { SetLastError(ERROR_INSUFFICIENT_BUFFER); return(0); } _wcslwr(pTemp); pString = pTemp; } break; } case ( GEO_LCID ) : { // // Check if the we have a valid LANGID. If not, retrieve // the default one. // if (LangId == 0) { LangId = GetUserDefaultLangID(); } // // Try to get a valid LCID from the GEOID and the LANGID. // if ((Locale = GetGeoLCID(GeoId, LangId)) == 0) { // // Try the Primary Language Identifier. // DefaultLangId = MAKELANGID(PRIMARYLANGID(LangId), SUBLANG_DEFAULT); if (DefaultLangId != LangId) { Locale = GetGeoLCID(GeoId, DefaultLangId); } // // Check if the Locale returned is valid. // if (Locale == 0) { // // Nothing found, make something with the LangId. // If Language ID already contains a sub-language, // we'll use it directly. // if (SUBLANGID(LangId) != 0) { Locale = MAKELCID(LangId, SORT_DEFAULT); } else { Locale = MAKELCID(MAKELANGID(LangId, SUBLANG_DEFAULT), SORT_DEFAULT); } } } // // Convert the value found into a string. // if (Locale != 0) { NlsConvertIntegerToString( Locale, 16, 8, pTemp, MAX_REG_VAL_SIZE ); pString = pTemp; } break; } case ( GEO_FRIENDLYNAME ) : { Length = GetStringTableEntry( GeoId, LangId, pTemp, MAX_REG_VAL_SIZE, RC_GEO_FRIENDLY_NAME ); if (Length == 0) { SetLastError(ERROR_INVALID_PARAMETER); return (0); } pString = pTemp; break; } case ( GEO_OFFICIALNAME ) : { Length = GetStringTableEntry( GeoId, LangId, pTemp, MAX_REG_VAL_SIZE, RC_GEO_OFFICIAL_NAME ); if (Length == 0) { // // If the official name is not there, fall back on // the friendly name. // Length = GetStringTableEntry( GeoId, LangId, pTemp, MAX_REG_VAL_SIZE, RC_GEO_FRIENDLY_NAME ); if (Length == 0) { SetLastError(ERROR_INVALID_PARAMETER); return (0); } } pString = pTemp; break; } case ( GEO_TIMEZONES ) : { // Not implemented break; } case ( GEO_OFFICIALLANGUAGES ) : { // Not implemented break; } default : { SetLastError(ERROR_INVALID_FLAGS); break; } } // // Make sure the pointer is valid. If not, return failure. // if (pString == NULL) { return (0); } // // Get the length (in characters) of the string to copy. // if (Length == 0) { Length = NlsStrLenW(pString); } // // Add one for null termination. All strings should be null // terminated. // Length++; // // Check cchData for size of given buffer. // if (cchData == 0) { // // If cchData is 0, then we can't use lpGeoData. In this // case, we simply want to return the length (in characters) of // the string to be copied. // return (Length); } else if (cchData < Length) { // // The buffer is too small for the string, so return an error // and zero bytes written. // SetLastError(ERROR_INSUFFICIENT_BUFFER); return (0); } // // Copy the string to lpGeoData and null terminate it. // Return the number of characters copied. // wcsncpy(lpGeoData, pString, Length - 1); lpGeoData[Length - 1] = 0; return (Length); } //////////////////////////////////////////////////////////////////////////// // // EnumSystemGeoID // // Enumerates the GEOIDs that are available on the system. This function // returns TRUE if it succeeds, FALSE if it fails. // //////////////////////////////////////////////////////////////////////////// BOOL WINAPI EnumSystemGeoID( GEOCLASS GeoClass, GEOID ParentGeoId, GEO_ENUMPROC lpGeoEnumProc) { int ctr1; if ((lpGeoEnumProc == NULL) || (0 != ParentGeoId) ) { SetLastError(ERROR_INVALID_PARAMETER); return (FALSE); } if (GeoClass != GEOCLASS_NATION) { SetLastError(ERROR_INVALID_FLAGS); return (FALSE); } if (pTblPtrs->pGeoInfo == NULL) { if (GetGeoFileInfo()) { return (FALSE); } } for (ctr1 = 0; ctr1 < pTblPtrs->nGeoInfo; ctr1++) { if (pTblPtrs->pGeoInfo[ctr1].GeoClass == GeoClass) { if (!lpGeoEnumProc(pTblPtrs->pGeoInfo[ctr1].GeoId)) { return (TRUE); } } } return (TRUE); } //////////////////////////////////////////////////////////////////////////// // // GetUserGeoID // // Retrieves information about the geographical location of the user. // This function returns a valid GEOID or the value GEOID_NOT_AVAILABLE. // //////////////////////////////////////////////////////////////////////////// GEOID WINAPI GetUserGeoID( GEOCLASS GeoClass) { PKEY_VALUE_FULL_INFORMATION pKeyValueFull; // ptr to query info BYTE buffer[MAX_KEY_VALUE_FULLINFO]; // buffer HANDLE hKey = NULL; // handle to geo key WCHAR wszGeoRegStr[48]; // ptr to class key GEOID GeoId = GEOID_NOT_AVAILABLE; // GEOID to default UNICODE_STRING ObUnicodeStr; // registry data value string switch (GeoClass) { case ( GEOCLASS_NATION ) : { if(FAILED(StringCchCopyW(wszGeoRegStr, ARRAYSIZE(wszGeoRegStr), GEO_REG_NATION))) { // // Failure should in theory be impossible, but if we ignore the // return value, PREfast will complain. // return(GEOID_NOT_AVAILABLE); } break; } case ( GEOCLASS_REGION ) : { if(FAILED(StringCchCopyW(wszGeoRegStr, ARRAYSIZE(wszGeoRegStr), GEO_REG_REGION))) { // // Failure should in theory be impossible, but if we ignore the // return value, PREfast will complain. // return(GEOID_NOT_AVAILABLE); } break; } default : { return (GeoId); } } // // Open the Control Panel International registry key. // OPEN_GEO_KEY(hKey, GEOID_NOT_AVAILABLE, KEY_READ); // // Query the registry value. // pKeyValueFull = (PKEY_VALUE_FULL_INFORMATION)buffer; if (QueryRegValue( hKey, wszGeoRegStr, &pKeyValueFull, MAX_KEY_VALUE_FULLINFO, NULL ) == NO_ERROR) { // // Convert the string to a value. // GeoId = _wtol(GET_VALUE_DATA_PTR(pKeyValueFull)); } // // Close the registry key. // CLOSE_REG_KEY(hKey); // // Return the Geo Id. // return (GeoId); } //////////////////////////////////////////////////////////////////////////// // // SetUserGeoID // // Sets information about the geographical location of the user. This // function returns TRUE if it succeeds, FALSE if it fails. // //////////////////////////////////////////////////////////////////////////// BOOL WINAPI SetUserGeoID( GEOID GeoId) { int ctr1, ctr2, ctr3; WCHAR wszRegStr[MAX_REG_VAL_SIZE]; HANDLE hKey = NULL; BOOL bRet = FALSE; WCHAR wszBuffer[MAX_REG_VAL_SIZE] = {0}; if (pTblPtrs->pGeoInfo == NULL) { if (GetGeoFileInfo()) { return (FALSE); } } ctr1 = 0; ctr2 = pTblPtrs->nGeoInfo - 1; ctr3 = ctr2 >> 1; // // Binary searching the GEOID's GEOCLASS type. // while (ctr1 <= ctr2) { if (GeoId == pTblPtrs->pGeoInfo[ctr3].GeoId) { switch (pTblPtrs->pGeoInfo[ctr3].GeoClass) { case ( GEOCLASS_NATION ) : { if(FAILED(StringCchCopyW(wszRegStr, ARRAYSIZE(wszRegStr), GEO_REG_NATION))) { // // Failure should in theory be impossible, but if we ignore the // return value, PREfast will complain. // return(FALSE); } break; } case ( GEOCLASS_REGION ) : { if(FAILED(StringCchCopyW(wszRegStr, ARRAYSIZE(wszRegStr), GEO_REG_REGION))) { // // Failure should in theory be impossible, but if we ignore the // return value, PREfast will complain. // return(FALSE); } break; } default : { return (FALSE); } } break; } else { if (GeoId < pTblPtrs->pGeoInfo[ctr3].GeoId) { ctr2 = ctr3 - 1; } else { ctr1 = ctr3 + 1; } ctr3 = (ctr1 + ctr2) >> 1; } } // // Not a valid GEOID or available GEOID if we can't find it in our // GEO table. // if (ctr1 > ctr2) { return (FALSE); } // // If the registry key does not exist, create a new one. // if (CreateRegKey( &hKey, NULL, GEO_REG_KEY, KEY_READ | KEY_WRITE ) != NO_ERROR) { return (FALSE); } // // Convert to decimal string. // NlsConvertIntegerToString((UINT)GeoId, 10, 0, wszBuffer, MAX_REG_VAL_SIZE); // // Set the new GEOID value. // if (SetRegValue( hKey, wszRegStr, wszBuffer, (NlsStrLenW(wszBuffer) + 1) * sizeof(WCHAR) ) == NO_ERROR) { bRet = TRUE; } // // Close the registry key. // CLOSE_REG_KEY(hKey); // // Return the result. // return (bRet); }