Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

412 lines
12 KiB

  1. /******************************************************************************
  2. Copyright (c) 2002 Microsoft Corporation
  3. Module Name:
  4. muiutil.cpp
  5. Abstract:
  6. Implements helper functions for self-updating MUI stuff
  7. ******************************************************************************/
  8. #include "pch.h"
  9. #include "muiutil.h"
  10. #include "osdet.h"
  11. typedef struct tagSLangIDStringMap
  12. {
  13. LANGID langid;
  14. LPTSTR szISOName;
  15. } SLangIDStringMap;
  16. /*
  17. // this is a combination of the languages used by WU and the languages used
  18. // by MUI
  19. // Mappings determined from the following sources
  20. // For langid to full language name : MSDN
  21. // full language name to 2 char name: http://www.oasis-open.org/cover/iso639a.html
  22. // country name to 2 character name : http://www.din.de/gremien/nas/nabd/iso3166ma
  23. This table is no longer used, but is kept as a reference for langid -> string
  24. mappings
  25. const SLangIDStringMap g_rgLangMap[] =
  26. {
  27. { 0x0401, _T("ar") },
  28. { 0x0402, _T("bg") },
  29. { 0x0403, _T("ca") },
  30. { 0x0404, _T("zhTW") },
  31. { 0x0405, _T("cs") },
  32. { 0x0406, _T("da") },
  33. { 0x0407, _T("de") },
  34. { 0x0408, _T("el") },
  35. { 0x0409, _T("en") },
  36. { 0x040b, _T("fi") },
  37. { 0x040c, _T("fr") },
  38. { 0x040d, _T("he") },
  39. { 0x040e, _T("hu") },
  40. { 0x0410, _T("it") },
  41. { 0x0411, _T("ja") },
  42. { 0x0412, _T("ko") },
  43. { 0x0413, _T("nl") },
  44. { 0x0414, _T("no") },
  45. { 0x0415, _T("pl") },
  46. { 0x0416, _T("ptBR") },
  47. { 0x0418, _T("ro") },
  48. { 0x0419, _T("ru") },
  49. { 0x041a, _T("hr") },
  50. { 0x041b, _T("sk") },
  51. { 0x041d, _T("sv") },
  52. { 0x041e, _T("en") },
  53. { 0x041f, _T("tr") },
  54. { 0x0424, _T("sl") },
  55. { 0x0425, _T("et") },
  56. { 0x0426, _T("lv") },
  57. { 0x0427, _T("lt") },
  58. { 0x042d, _T("eu") },
  59. { 0x0804, _T("zhCN") },
  60. { 0x080a, _T("es") },
  61. { 0x0816, _T("pt") },
  62. { 0x0c0a, _T("es") }
  63. };
  64. */
  65. // ******************************************************************************
  66. BOOL MapLangIdToStringName(LANGID langid, LPCTSTR pszIdentFile,
  67. LPTSTR pszLangString, DWORD cchLangString)
  68. {
  69. LOG_Block("MapLangIdToStringName");
  70. TCHAR szLang[32];
  71. DWORD cch;
  72. LCID lcid;
  73. BOOL fRet = FALSE;
  74. lcid = MAKELCID(langid, SORT_DEFAULT);
  75. fRet = LookupLocaleStringFromLCID(lcid, szLang, ARRAYSIZE(szLang));
  76. if (fRet == FALSE)
  77. {
  78. LOG_ErrorMsg(GetLastError());
  79. goto done;
  80. }
  81. // first try the whole string ("<lang>-<country>")
  82. cch = GetPrivateProfileString(IDENT_LANG, szLang,
  83. _T(""),
  84. pszLangString, cchLangString,
  85. pszIdentFile);
  86. if (cch == cchLangString - 1)
  87. {
  88. SetLastError(ERROR_INSUFFICIENT_BUFFER);
  89. LOG_ErrorMsg(ERROR_INSUFFICIENT_BUFFER);
  90. goto done;
  91. }
  92. // if that fails, strip off the country code & just try the language
  93. else if (cch == 0)
  94. {
  95. LPTSTR pszDash;
  96. pszDash = StrChr(szLang, _T('-'));
  97. if (pszDash != NULL)
  98. {
  99. *pszDash = _T('\0');
  100. cch = GetPrivateProfileString(IDENT_LANG, szLang,
  101. _T(""),
  102. pszLangString, cchLangString,
  103. pszIdentFile);
  104. if (cch == cchLangString - 1)
  105. {
  106. SetLastError(ERROR_INSUFFICIENT_BUFFER);
  107. LOG_ErrorMsg(ERROR_INSUFFICIENT_BUFFER);
  108. goto done;
  109. }
  110. }
  111. }
  112. if (cch > 0 && pszLangString[0] == _T('/'))
  113. {
  114. // want to use the full cch (& not cch - 1) because we want to copy the
  115. // NULL terminator also...
  116. MoveMemory(&pszLangString[0], &pszLangString[1], cch * sizeof(TCHAR));
  117. }
  118. fRet = TRUE;
  119. done:
  120. return fRet;
  121. }
  122. // ******************************************************************************
  123. BOOL CALLBACK EnumUILangProc(LPTSTR szUILang, LONG_PTR lParam)
  124. {
  125. LOG_Block("EnumUILangProc");
  126. AU_LANGLIST *paull = (AU_LANGLIST *)lParam;
  127. AU_LANG *paulNew = NULL;
  128. HRESULT hr;
  129. LANGID langid;
  130. LPTSTR pszStop;
  131. TCHAR szAUName[32];
  132. DWORD cchMuiName, cchAUName, cbNeed, cchAvail, dwLangID;
  133. BOOL fRet = FALSE, fMap;
  134. if (szUILang == NULL || lParam == NULL)
  135. goto done;
  136. langid = (LANGID)_tcstoul(szUILang, &pszStop, 16);
  137. // if we don't have a mapping for a langid, then just skip the language
  138. // and return success
  139. szAUName[0] = _T('\0');
  140. fMap = MapLangIdToStringName(langid, paull->pszIdentFile,
  141. szAUName, ARRAYSIZE(szAUName));
  142. if (fMap == FALSE || szAUName[0] == _T('\0'))
  143. {
  144. fRet = TRUE;
  145. goto done;
  146. }
  147. if (paull->cLangs >= paull->cSlots)
  148. {
  149. AU_LANG **rgpaulNew = NULL;
  150. DWORD cNewSlots = paull->cSlots * 2;
  151. if (cNewSlots == 0)
  152. cNewSlots = 32;
  153. if (paull->rgpaulLangs != NULL)
  154. {
  155. rgpaulNew = (AU_LANG **)HeapReAlloc(GetProcessHeap(),
  156. HEAP_ZERO_MEMORY,
  157. paull->rgpaulLangs,
  158. cNewSlots * sizeof(AU_LANG *));
  159. }
  160. else
  161. {
  162. rgpaulNew = (AU_LANG **)HeapAlloc(GetProcessHeap(),
  163. HEAP_ZERO_MEMORY,
  164. cNewSlots * sizeof(AU_LANG *));
  165. }
  166. if (rgpaulNew == NULL)
  167. goto done;
  168. paull->rgpaulLangs = rgpaulNew;
  169. paull->cSlots = cNewSlots;
  170. }
  171. // we will be adding an '_' to the beginning of the AUName, so make sure
  172. // the size we compute here reflects that.
  173. cchAUName = lstrlen(szAUName) + 1;
  174. cchMuiName = lstrlen(szUILang);
  175. // alloc a buffer to hold the AU_LANG struct plus the two strings (and
  176. // don't forget the NULL terminators!).
  177. // The layout of the buffer is as follows:
  178. // <AU_LANG>
  179. // <szMuiName>
  180. // _<szAUName>
  181. // NOTE: if this buffer format ever change, gotta make sure that the
  182. // contents are aligned properly (otherwise, we'll fault on ia64)
  183. cbNeed = sizeof(AU_LANG);
  184. cbNeed += ((cchMuiName + cchAUName + 2) * sizeof(TCHAR));
  185. paulNew = (AU_LANG *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbNeed);
  186. if (paulNew == NULL)
  187. goto done;
  188. paulNew->szMuiName = (LPTSTR)((PBYTE)paulNew + sizeof(AU_LANG));
  189. paulNew->szAUName = paulNew->szMuiName + cchMuiName + 1;
  190. // this should never truncate the buffers cuz we calc'ed the size above and
  191. // allocated a buffer exactly long enuf to hold all of this
  192. cchAvail = (cbNeed - sizeof(AU_LANG)) / sizeof(TCHAR);
  193. hr = StringCchCopyEx(paulNew->szMuiName, cchAvail, szUILang,
  194. NULL, NULL, MISTSAFE_STRING_FLAGS);
  195. if (FAILED(hr))
  196. goto done;
  197. cchAvail -= (cchMuiName + 1);
  198. // need to put an '_' in front of the AU name, so add it to the buffer and
  199. // reduce the available size by one. Also make sure to start copying the
  200. // AUName *after* the '_' character.
  201. paulNew->szAUName[0] = _T('_');
  202. cchAvail--;
  203. hr = StringCchCopyEx(&paulNew->szAUName[1], cchAvail, szAUName,
  204. NULL, NULL, MISTSAFE_STRING_FLAGS);
  205. if (FAILED(hr))
  206. goto done;
  207. paull->rgpaulLangs[paull->cLangs++] = paulNew;
  208. paulNew = NULL;
  209. fRet = TRUE;
  210. done:
  211. if (paulNew != NULL)
  212. HeapFree(GetProcessHeap(), 0, paulNew);
  213. return fRet;
  214. }
  215. // ******************************************************************************
  216. HRESULT GetMuiLangList(AU_LANGLIST *paull,
  217. LPTSTR pszMuiDir, DWORD *pcchMuiDir,
  218. LPTSTR pszHelpMuiDir, DWORD *pcchHelpMuiDir)
  219. {
  220. LOG_Block("GetMuiLangList");
  221. HRESULT hr = NOERROR;
  222. DWORD cMuiLangs;
  223. int iLang;
  224. paull->cLangs = 0;
  225. paull->cSlots = 0;
  226. paull->rgpaulLangs = NULL;
  227. if (EnumUILanguages(EnumUILangProc, 0, (LONG_PTR)paull) == FALSE)
  228. {
  229. hr = HRESULT_FROM_WIN32(GetLastError());
  230. goto done;
  231. }
  232. // We need to deal with MUI stuff only if we've have more than 0 languages
  233. // to worry about
  234. if (paull->cLangs > 0)
  235. {
  236. LPTSTR pszHelp = NULL, pszMui = NULL;
  237. size_t cchAvail, cchAvailHelp;
  238. TCHAR szPath[MAX_PATH + 1];
  239. DWORD dwAttrib;
  240. DWORD cch, cLangs = (int)paull->cLangs;
  241. BOOL fDeleteLang;
  242. // need to get the directory we'll stuff MUI updates into
  243. cch = GetSystemWindowsDirectory(pszMuiDir, *pcchMuiDir);
  244. // note 2nd compare takes into account the addition of an extra '\\' after
  245. // the system windows dir
  246. if (cch == 0 || cch >= *pcchMuiDir)
  247. {
  248. hr = HRESULT_FROM_WIN32(GetLastError());
  249. goto done;
  250. }
  251. // tack on an extra '\\' if necessary
  252. if (pszMuiDir[cch - 1] != _T('\\'))
  253. {
  254. pszMuiDir[cch++] = _T('\\');
  255. pszMuiDir[cch] = _T('\0');
  256. }
  257. hr = StringCchCopyEx(pszHelpMuiDir, *pcchHelpMuiDir, pszMuiDir,
  258. NULL, NULL, MISTSAFE_STRING_FLAGS);
  259. if (FAILED(hr))
  260. goto done;
  261. hr = StringCchCatEx(pszHelpMuiDir, *pcchHelpMuiDir, MUI_HELPSUBDIR,
  262. &pszHelp, &cchAvailHelp, MISTSAFE_STRING_FLAGS);
  263. if (FAILED(hr))
  264. goto done;
  265. hr = StringCchCatEx(pszMuiDir, *pcchMuiDir, MUI_SUBDIR,
  266. &pszMui, &cchAvail, MISTSAFE_STRING_FLAGS);
  267. if (FAILED(hr))
  268. goto done;
  269. *pcchMuiDir -= cchAvail;
  270. *pcchHelpMuiDir -= cchAvailHelp;
  271. // check and make sure that the MUI directories exist- remove all those that
  272. // do not. This section also checks to make sure that the buffer passed in
  273. // is large enuf to hold the language
  274. for(iLang = (int)(cLangs - 1); iLang >= 0; iLang--)
  275. {
  276. fDeleteLang = FALSE;
  277. hr = StringCchCopyEx(pszMui, cchAvail,
  278. paull->rgpaulLangs[iLang]->szMuiName,
  279. NULL, NULL, MISTSAFE_STRING_FLAGS);
  280. if (FAILED(hr))
  281. goto done;
  282. dwAttrib = GetFileAttributes(pszMuiDir);
  283. if (dwAttrib == INVALID_FILE_ATTRIBUTES ||
  284. (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) == 0)
  285. {
  286. fDeleteLang = TRUE;
  287. }
  288. else
  289. {
  290. hr = StringCchCopyEx(pszHelp, cchAvailHelp,
  291. paull->rgpaulLangs[iLang]->szMuiName,
  292. NULL, NULL, MISTSAFE_STRING_FLAGS);
  293. if (FAILED(hr))
  294. goto done;
  295. dwAttrib = GetFileAttributes(pszHelpMuiDir);
  296. if (dwAttrib == INVALID_FILE_ATTRIBUTES ||
  297. (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) == 0)
  298. {
  299. fDeleteLang = TRUE;
  300. }
  301. }
  302. if (fDeleteLang)
  303. {
  304. HeapFree(GetProcessHeap(), 0, paull->rgpaulLangs[iLang]);
  305. if (iLang != paull->cLangs - 1)
  306. {
  307. MoveMemory(&paull->rgpaulLangs[iLang],
  308. &paull->rgpaulLangs[iLang + 1],
  309. (paull->cLangs - iLang - 1) * sizeof(AU_LANG *));
  310. }
  311. paull->rgpaulLangs[--paull->cLangs] = NULL;
  312. }
  313. }
  314. pszMuiDir[*pcchMuiDir] = _T('\0');
  315. pszHelpMuiDir[*pcchHelpMuiDir] = _T('\0');
  316. }
  317. done:
  318. if (FAILED(hr))
  319. CleanupMuiLangList(paull);
  320. return hr;
  321. }
  322. // ******************************************************************************
  323. HRESULT CleanupMuiLangList(AU_LANGLIST *paull)
  324. {
  325. LOG_Block("CleanupMuiLangList");
  326. HRESULT hr = S_OK;
  327. DWORD i;
  328. // if it's NULL, just return success
  329. if (paull == NULL)
  330. return hr;
  331. if (paull->rgpaulLangs == NULL)
  332. goto done;
  333. for (i = 0; i < paull->cLangs; i++)
  334. {
  335. if (paull->rgpaulLangs[i] != NULL)
  336. HeapFree(GetProcessHeap(), 0, paull->rgpaulLangs[i]);
  337. }
  338. HeapFree(GetProcessHeap(), 0, paull->rgpaulLangs);
  339. done:
  340. ZeroMemory(paull, sizeof(AU_LANGLIST));
  341. return hr;
  342. }