[Tizen] Unify dnetmemoryenumlib terms to match the codebase (#291)
[platform/upstream/coreclr.git] / src / utilcode / loadrc-impl.cpp
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4
5 //
6 // LoadRC-impl.cpp : Utility to load a localized file (primarily a DLL)
7
8 #include "palclr.h"
9
10 #if defined(USE_SSTRING)
11
12 // This is the normal path:  use SafeString, wrappers, etc...
13
14 #include "sstring.h"
15 #include "safewrap.h"
16
17 typedef SString MyString;
18 typedef SString::CIterator MyStringIterator;
19 #define EndsWithChar(OBJ, CHAR) (OBJ.EndsWith(CHAR))
20 #define AppendChar(OBJ, CHAR) (OBJ.Append(CHAR))
21 #define AppendStr(OBJ, STR) (OBJ.Append(STR))
22 #define TrimLastChar(OBJ) (OBJ.Truncate(OBJ.End() - 1))
23 #define GetChars(OBJ) (OBJ.GetUnicode())
24 #define IsEmptyStr(OBJ) (OBJ.IsEmpty())
25 #define CharLength(OBJ) (OBJ.GetCount())
26 #define StrBeginIter(OBJ) (OBJ.Begin())
27 #define StrEndIter(OBJ) (OBJ.End())
28 #define FindNext(OBJ, ITER, CHAR) (OBJ.Find(ITER, CHAR))
29 #define MakeString(DST, OBJ, BEG, END) (DST.Set(OBJ, BEG, END))
30 #define StrEquals(STR1, STR2) (STR1.Compare(STR2)==0)
31 #define FindLast(OBJ, ITER, CHAR) (OBJ.FindBack(ITER, CHAR))
32 void SkipChars(MyString &str, MyStringIterator &i, WCHAR c1, WCHAR c2) { while (str.Skip(i, c1) || str.Skip(i, c2)); }
33
34 #elif defined(USE_WSTRING)
35
36 // This stuff is used by GacUtil, because it _really_ doesn't want to link with utilcode :-(
37
38 #include <string>
39 #include <algorithm>
40 typedef std::wstring MyString;
41 typedef std::wstring::const_iterator MyStringIterator;
42 #define EndsWithChar(OBJ, CHAR) (*(OBJ.rbegin()) == CHAR)
43 #define AppendChar(OBJ, CHAR) (OBJ.push_back(CHAR))
44 #define AppendStr(OBJ, STR) (OBJ += STR)
45 #define TrimLastChar(OBJ) (OBJ.resize(OBJ.size() - 1))
46 #define GetChars(OBJ) (OBJ.c_str())
47 #define IsEmptyStr(OBJ) (OBJ.empty())
48 #define CharLength(OBJ) (OBJ.size())
49 #define StrBeginIter(OBJ) (OBJ.begin())
50 #define StrEndIter(OBJ) (OBJ.end())
51 #define FindNext(OBJ, ITER, CHAR) (ITER = std::find<std::wstring::const_iterator>(ITER, OBJ.end(), CHAR))
52 #define MakeString(DST, OBJ, BEG, END) (DST = MyString(BEG, END))
53 #define StrEquals(STR1, STR2) (STR1 == STR2)
54 #define ClrGetEnvironmentVariable(var, res) GetEnvVar(L##var, res)
55 bool FindLast(const MyString &str, MyStringIterator &iter, wchar_t c)
56 {
57     size_t pos = str.find_last_of(c);
58     iter = (pos == std::wstring::npos) ? str.end() : (str.begin() + pos);
59     return pos != std::wstring::npos;
60 }
61 void SkipChars(const MyString &str, MyStringIterator &i, WCHAR c1, WCHAR c2) { while (*i == c1 || *i == c2) i++; }
62 bool GetEnvVar(_In_z_ wchar_t *var, MyString &res)
63 {
64     wchar_t *buffer;
65     size_t size;
66     _wdupenv_s(&buffer, &size, var);
67     if (!size || !buffer)
68         return false;
69     res = buffer;
70     free(buffer); // Don't forget to free the buffer!
71     return true;
72 }
73 void ClrGetModuleFileName(HMODULE hModule, MyString& value)
74 {
75     wchar_t driverpath_tmp[_MAX_PATH];
76     GetModuleFileNameW(hModule, driverpath_tmp, _MAX_PATH);
77     value = driverpath_tmp;
78 }
79
80 #else
81
82 #error You must define either USE_SSTRING or USE_WSTRING to use this file
83
84 #endif
85
86 // This is a helper for loading localized string resource DLL files
87 HMODULE LoadLocalizedResourceDLLForSDK(_In_z_ LPCWSTR wzResourceDllName, _In_opt_z_ LPCWSTR modulePath, bool trySelf);
88
89 // This is a slight variation that can be used for anything else (ildasm.chm, for example)
90 typedef void* (__cdecl *LocalizedFileHandler)(LPCWSTR);
91 void* FindLocalizedFile(_In_z_ LPCWSTR wzResourceDllName, LocalizedFileHandler lfh, _In_opt_z_ LPCWSTR modulePath);
92
93 // Helper functions to combine paths
94 static MyString MakePath(const MyString &root, const MyString &file)
95 {
96     MyString res = root;
97     if (!EndsWithChar(res, W('\\')))
98         AppendChar(res, W('\\'));
99     AppendStr(res, file);
100     return res;
101 }
102 static MyString MakePath(const MyString &root, const MyString &dir, const MyString &file)
103 {
104     return MakePath(MakePath(root, dir), file);
105 }
106
107 // Helper to deal with occasional training back-slashes
108 static bool FileExists(const MyString &file)
109 {
110     if (!EndsWithChar(file, W('\\')))
111         return GetFileAttributesW(GetChars(file)) != INVALID_FILE_ATTRIBUTES;
112     else
113     {
114         MyString tmp(file);
115         TrimLastChar(tmp);
116         return GetFileAttributesW(GetChars(tmp)) != INVALID_FILE_ATTRIBUTES;
117     }
118 }
119
120 // Little helper function to get the codepage integer ID from the LocaleInfo
121 static UINT GetCodePage(LANGID LanguageID, DWORD locale)
122 {    
123     wchar_t CodePageInt[12];
124     GetLocaleInfo(MAKELCID(LanguageID, SORT_DEFAULT), LOCALE_IDEFAULTCODEPAGE, CodePageInt, _countof(CodePageInt));
125     return _wtoi(CodePageInt);
126 }
127
128 // LCID helper macro
129 #define ENGLISH_LCID MAKELCID(MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US ), SORT_DEFAULT)
130
131 // FindLocaleDirectory:  Search the provided path for one of the expected code page subdirectories
132 // Returns empty string on failure, or the full path to the c:\my\directory\1033\myrcfile.dll
133 static MyString FindLocaleDirectory(const MyString &path, const MyString &dllName)
134 {
135     // We'll be checking for 3 different locales:  The user's default locale
136     // The user's primary language locale, and english (in that order)
137     const LCID lcidUser = MAKELCID(GetUserDefaultUILanguage(), SORT_DEFAULT);
138     LCID rglcid[3] = {lcidUser, MAKELCID(MAKELANGID(PRIMARYLANGID(lcidUser), SUBLANG_DEFAULT), SORTIDFROMLCID(lcidUser)), ENGLISH_LCID};
139
140     for (int i = 0; i < _countof(rglcid); i++)
141     {
142         LCID lcid = rglcid[i];
143         // Turn the LCID into a string
144         wchar_t wzNumBuf[12];
145         _itow_s(lcid, wzNumBuf, _countof(wzNumBuf), 10);
146         MyString localePath = MakePath(path, wzNumBuf, dllName);
147
148         // Check to see if the file exists
149         if (FileExists(localePath))
150         {
151             // make sure the console can support a codepage for this language.
152             UINT ConsoleCP = GetConsoleOutputCP();
153             
154             // Dev10 #843375: For a GUI application, GetConsoleOutputCP returns 0
155             // If that's the case, we don't care about capabilities of the console, 
156             // since we're not outputting to the console, anyway...
157             if ( ConsoleCP != 0 && lcid != ENGLISH_LCID )
158             {
159                 LANGID LanguageID = MAKELANGID( lcid, SUBLANGID(lcid) );
160                 // we know the console cannot support arabic or hebrew (right to left scripts?)
161                 if( PRIMARYLANGID(LanguageID) == LANG_ARABIC || PRIMARYLANGID(LanguageID) == LANG_HEBREW )
162                     continue;
163
164                 UINT LangOEMCodepage = GetCodePage(LanguageID, LOCALE_IDEFAULTCODEPAGE);
165                 UINT LangANSICodepage = GetCodePage(LanguageID, LOCALE_IDEFAULTANSICODEPAGE);
166
167                 // We can only support it if the console's code page is UTF8, OEM, or ANSI
168                 if( ConsoleCP != CP_UTF8 && ConsoleCP != LangOEMCodepage && ConsoleCP != LangANSICodepage )
169                     continue;
170             }
171
172             return localePath;
173         }
174     }
175     return W("");
176 }
177
178 // Attempt to load the resource file from the locale, first.
179 // If that fails, then just try any subdirectory of of the path provided
180 static void *LoadLocalFile(const MyString &path, const MyString &dllName, LocalizedFileHandler lfh)
181 {
182     if (IsEmptyStr(path) || IsEmptyStr(dllName))
183         return NULL;
184
185     MyString pathTemp = path;
186
187     // Languages are checked in the following order.  
188     //    1)  The UI language:  this is returned by GetUserDefaultUILanguage.
189     //    2)  As step 1, but with SUBLANG_DEFAULT
190     //    3)  English
191     //    4)  Any language that can be found!
192
193     MyString localePath = FindLocaleDirectory(pathTemp, dllName);
194
195     if (IsEmptyStr(localePath))
196     {
197         // None of the default choices exists, so now look for the first version of the dll in the given path.
198         // We don't bother to see if the console supports the dll's language.
199             MyString wildCard = MakePath(pathTemp, W("*.*"));
200             WIN32_FIND_DATAW    wfdw;
201         HANDLE hDirs = FindFirstFileW(GetChars(wildCard), &wfdw);
202         if (hDirs == INVALID_HANDLE_VALUE)
203             return NULL;
204         do 
205         {
206             // We are only interested in directories, since at this level, that should
207             // be the only thing in this directory, i.e, LCID sub dirs
208             if (wfdw.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
209             {
210                 MyString file(wfdw.cFileName);
211
212                 if (StrEquals(file, W(".")))
213                     continue;
214
215                 if (StrEquals(file, W("..")))
216                     continue;
217
218                 // Does this dir have the resource dll?
219                 MyString fullPath = MakePath(pathTemp, file, dllName);
220
221                 if (GetFileAttributesW(GetChars(fullPath)) != INVALID_FILE_ATTRIBUTES)
222                 {
223                     localePath = fullPath; // Got it - bail out of here
224                     break;
225                 }
226             }
227         } while (FindNextFileW(hDirs, &wfdw));
228
229         FindClose(hDirs);
230
231
232         if (IsEmptyStr(localePath))
233         {
234             //
235             // With CoreCLR we have the resource dll directly in the bin directory so check there now.
236             //
237             
238             // Does this dir have the resource dll?
239             MyString fullPath = MakePath(path, dllName);
240
241             if (GetFileAttributesW(GetChars(fullPath)) != INVALID_FILE_ATTRIBUTES)
242             {
243                 localePath = fullPath; // Got it - bail out of here
244             }
245         }
246     }
247
248     // Attempt to load the library
249     // Beware!  A dll loaded with LOAD_LIBRARY_AS_DATAFILE won't
250     // let you use LoadIcon and things like that (only general calls like FindResource and LoadResource).
251     return IsEmptyStr(localePath) ? NULL : lfh(GetChars(localePath));
252 }
253
254 // Try to load the resource DLL from [each directory in %PATH%]/<lcid>/
255 static void *LoadSearchPath(const MyString &resourceDllName, LocalizedFileHandler lfh)
256 {
257     void *hmod = NULL;
258
259     // Get the PATH variable into a C++ string
260     MyString envPath;
261
262     if (ClrGetEnvironmentVariable("PATH", envPath))
263         return hmod;
264
265     MyStringIterator  endOfChunk, startOfChunk = StrBeginIter(envPath);
266     MyString tryPath;
267     for (SkipChars(envPath, startOfChunk, W(' '), W(';')); 
268         hmod == NULL && startOfChunk != StrEndIter(envPath);
269         SkipChars(envPath, startOfChunk, W(' '), W(';')))
270     {
271         // copy this chunk of the path into our trypath
272         endOfChunk = startOfChunk;
273         FindNext(envPath, endOfChunk, W(';'));
274         MakeString(tryPath, envPath, startOfChunk, endOfChunk);
275
276         // Don't try invalid locations
277         if (IsEmptyStr(tryPath) || CharLength(tryPath) >= _MAX_PATH)
278             continue;
279
280         // Try to load the dll
281         hmod = LoadLocalFile(tryPath, resourceDllName, lfh);
282         startOfChunk = endOfChunk;
283     }
284     return hmod;
285 }
286
287 void * __cdecl LibraryLoader(_In_z_ LPCWSTR lpFileName)
288 {
289     return (void *)(LoadLibraryExW(lpFileName, NULL, LOAD_LIBRARY_AS_DATAFILE));
290 }
291
292 void *FindLocalizedFile(_In_z_ LPCWSTR wzResourceDllName, LocalizedFileHandler lfh, _In_opt_z_ LPCWSTR modulePathW)
293 {
294     // find path of the modulePath
295     MyString driverPath;
296     MyString modulePath;
297     ClrGetModuleFileName(GetModuleHandleW(modulePathW), modulePath);
298
299     // Rip off the application name.
300     MyStringIterator trailingSlashLocation = StrEndIter(modulePath);
301     if (FindLast(modulePath, trailingSlashLocation, W('\\')))
302         MakeString(driverPath, modulePath, StrBeginIter(modulePath), trailingSlashLocation);
303     else
304         // If it's not a full path, look in the current directory
305         driverPath = W(".");
306
307     // return the first of the local directory's copy or the resource DLL on %PATH%
308     void *hmod = LoadLocalFile(driverPath, wzResourceDllName, lfh);
309     if (hmod == NULL)
310         hmod = LoadSearchPath(wzResourceDllName, lfh);
311     return hmod;
312 }
313
314 // load the satellite dll which contains string resources
315 HMODULE LoadLocalizedResourceDLLForSDK(_In_z_ LPCWSTR wzResourceDllName, _In_opt_z_ LPCWSTR modulePath, bool trySelf)
316 {
317     HMODULE hmod = (HMODULE)FindLocalizedFile(wzResourceDllName, &LibraryLoader, modulePath);
318     if (hmod == NULL && trySelf)
319         hmod = GetModuleHandleW(NULL);
320     return hmod;
321 }