1 /*
2    Copyright (c) 2002 Perry Rapp
3    "The MIT license"
4    Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5    The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
7 */
8 /*=============================================================
9  * intlshim.c -- Shim to connect to gettext dll if available
10  * Windows specific
11  *   Created: 2002/06 by Perry Rapp
12  *   Edited:  2002/11/20 (Perry Rapp)
13  *==============================================================*/
14 
15 #include "intlshim.h"
16 #include <windows.h>
17 #include <stdio.h>
18 
19 #ifndef INTLDECL
20 #define INTLDECL
21 #endif
22 
23 #define INTLSHIM_VERSION "1.1.1"
24 
25 
26 static FARPROC MyGetProcAddress(HMODULE hModule, LPCSTR lpProcName);
27 static int ishim_get_dll_name(char * filepath, int pathlen);
28 static int ishim_get_file_version(const char * filepath, char * verout, int veroutlen);
29 static int ishim_set_dll_name(const char *filepath);
30 static int load_dll(void);
31 static void unload_dll(void);
32 
33 static HINSTANCE f_hinstDll=0;
34 static int f_failed=0;
35 static char f_dllpath[MAX_PATH]="";
36 static char * f_defaults[] = { "gettext.dll", "libintl.dll", "intl.dll" };
37 
38 
39 typedef char * (*gettext_type)(const char *m);
40 typedef char * (*dgettext_type)(const char *dom, const char *m);
41 typedef char * (*dcgettext_type)(const char *dom, const char *m, int cat);
42 typedef char * (*ngettext_type)(const char *__msgid1, const char *__msgid2, unsigned long int __n);
43 typedef char * (*dngettext_type)(const char *dom, const char *m1, const char *m2, unsigned long int n);
44 typedef char * (*dcngettext_type)(const char *__domainname, const char *m1, const char *m2, unsigned long int n, int cat);
45 typedef char * (*textdomain_type)(const char *dom);
46 typedef char * (*bindtextdomain_type)(const char *dom, const char *dir);
47 typedef char * (*bind_textdomain_codeset_type)(const char *dom, const char *cs);
48 typedef int (*gt_notify_language_change_type)(void);
49 typedef int (*gt_get_property_type)(const char *name, char *value, int valuelen);
50 typedef int (*gt_set_property_type)(const char *name, const char *value);
51 
52 static struct gettext_fncs_s
53 {
54 	gettext_type gettext_x;
55 	dgettext_type dgettext_x;
56 	dcgettext_type dcgettext_x;
57 	ngettext_type ngettext_x;
58 	dngettext_type dngettext_x;
59 	dcngettext_type dcngettext_x;
60 	textdomain_type textdomain_x;
61 	bindtextdomain_type bindtextdomain_x;
62 	bind_textdomain_codeset_type bind_textdomain_codeset_x;
63 	gt_notify_language_change_type gt_notify_language_change_x;
64 	gt_get_property_type gt_get_property_x;
65 	gt_set_property_type gt_set_property_x;
66 } f_gettext_fncs;
67 
68 
69 /* Look up MSGID in the current default message catalog for the current
70    LC_MESSAGES locale.  If not found, returns MSGID itself (the default
71    text).  */
72 INTLDECL char *
gettext(const char * __msgid)73 gettext(const char *__msgid)
74 {
75 	if (!load_dll() || !f_gettext_fncs.gettext_x)
76 		return (char *)__msgid;
77 	return (*f_gettext_fncs.gettext_x)(__msgid);
78 }
79 
80 /* Look up MSGID in the DOMAINNAME message catalog for the current
81    LC_MESSAGES locale.  */
82 INTLDECL char *
dgettext(const char * __domainname,const char * __msgid)83 dgettext(const char *__domainname,
84 				     const char *__msgid)
85 {
86 	if (!load_dll() || !f_gettext_fncs.dgettext_x)
87 		return (char *)__msgid;
88 	return (*f_gettext_fncs.dgettext_x)(__domainname, __msgid);
89 }
90 
91 /* Look up MSGID in the DOMAINNAME message catalog for the current CATEGORY
92    locale.  */
93 INTLDECL char *
dcgettext(const char * __domainname,const char * __msgid,int __category)94 dcgettext(const char *__domainname,
95 				      const char *__msgid,
96 				      int __category)
97 {
98 	if (!load_dll() || !f_gettext_fncs.dcgettext_x)
99 		return (char *)__msgid;
100 	return (*f_gettext_fncs.dcgettext_x)(__domainname, __msgid, __category);
101 }
102 
103 /* Similar to `gettext' but select the plural form corresponding to the
104    number N.  */
105 INTLDECL char *
ngettext(const char * __msgid1,const char * __msgid2,unsigned long int __n)106 ngettext(const char *__msgid1,
107 				     const char *__msgid2,
108 				     unsigned long int __n)
109 {
110 	if (!load_dll() || !f_gettext_fncs.ngettext_x)
111 		return __n>1 ? (char *)__msgid2 : (char *)__msgid1;
112 	return (*f_gettext_fncs.ngettext_x)(__msgid1, __msgid2, __n);
113 }
114 
115 /* Similar to `dgettext' but select the plural form corresponding to the
116    number N.  */
117 INTLDECL char *
dngettext(const char * __domainname,const char * __msgid1,const char * __msgid2,unsigned long int __n)118 dngettext(const char *__domainname,
119 				      const char *__msgid1,
120 				      const char *__msgid2,
121 				      unsigned long int __n)
122 {
123 	if (!load_dll() || !f_gettext_fncs.dngettext_x)
124 		return __n>1 ? (char *)__msgid2 : (char *)__msgid1;
125 	return (*f_gettext_fncs.dngettext_x)(__domainname, __msgid1, __msgid2, __n);
126 }
127 
128 /* Similar to `dcgettext' but select the plural form corresponding to the
129    number N.  */
130 INTLDECL char *
dcngettext(const char * __domainname,const char * __msgid1,const char * __msgid2,unsigned long int __n,int __category)131 dcngettext(const char *__domainname,
132 				       const char *__msgid1,
133 				       const char *__msgid2,
134 				       unsigned long int __n,
135 				       int __category)
136 {
137 	if (!load_dll() || !f_gettext_fncs.dcngettext_x)
138 		return __n>1 ? (char *)__msgid2 : (char *)__msgid1;
139 	return (*f_gettext_fncs.dcngettext_x)(__domainname, __msgid1, __msgid2, __n, __category);
140 }
141 
142 /* Set the current default message catalog to DOMAINNAME.
143    If DOMAINNAME is null, return the current default.
144    If DOMAINNAME is "", reset to the default of "messages".  */
145 INTLDECL char *
textdomain(const char * __domainname)146 textdomain(const char *__domainname)
147 {
148 	if (!load_dll() || !f_gettext_fncs.textdomain_x)
149 		return 0;
150 	return (*f_gettext_fncs.textdomain_x)(__domainname);
151 }
152 
153 /* Specify that the DOMAINNAME message catalog will be found
154    in DIRNAME rather than in the system locale data base.  */
155 INTLDECL char *
bindtextdomain(const char * __domainname,const char * __dirname)156 bindtextdomain(const char *__domainname,
157 					   const char *__dirname)
158 {
159 	if (!load_dll() || !f_gettext_fncs.bindtextdomain_x)
160 		return 0;
161 	return (*f_gettext_fncs.bindtextdomain_x)(__domainname, __dirname);
162 }
163 
164 /* Specify the character encoding in which the messages from the
165    DOMAINNAME message catalog will be returned.  */
166 INTLDECL char *
bind_textdomain_codeset(const char * __domainname,const char * __codeset)167 bind_textdomain_codeset(const char *__domainname,
168 						    const char *__codeset)
169 {
170 	if (!load_dll() || !f_gettext_fncs.bind_textdomain_codeset_x)
171 		return 0;
172 	return (*f_gettext_fncs.bind_textdomain_codeset_x)(__domainname, __codeset);
173 }
174 
175 /* increment nl_msg_cat_cntr in gettext */
176 INTLDECL void
gt_notify_language_change(void)177 gt_notify_language_change (void)
178 {
179 	/*
180 	TODO: Need to find out how to directly bump counter
181 	in case this is someone else's gettext.dll
182 	*/
183 	if (!load_dll() || !f_gettext_fncs.gt_notify_language_change_x)
184 		return;
185 	(*f_gettext_fncs.gt_notify_language_change_x)();
186 }
187 
188 /* pass some get_property call to gettext */
189 INTLDECL int
gt_get_property(const char * name,char * value,int valuelen)190 gt_get_property (const char *name, char *value, int valuelen)
191 {
192 	if (!load_dll() || !f_gettext_fncs.gt_get_property_x)
193 		return 0;
194 	return (*f_gettext_fncs.gt_get_property_x)(name, value, valuelen);
195 }
196 
197 /* pass some set_property call to gettext */
198 INTLDECL int
gt_set_property(const char * name,const char * value)199 gt_set_property (const char *name, const char *value)
200 {
201 	if (!load_dll() || !f_gettext_fncs.gt_set_property_x)
202 		return 0;
203 	return (*f_gettext_fncs.gt_set_property_x)(name, value);
204 }
205 
206 static void
unload_dll(void)207 unload_dll (void)
208 {
209 	if (!f_hinstDll)
210 		return;
211 	memset(&f_gettext_fncs, 0, sizeof(f_gettext_fncs));
212 	FreeLibrary(f_hinstDll);
213 	f_hinstDll = 0;
214 }
215 
216 static FARPROC
MyGetProcAddress(HMODULE hModule,LPCSTR lpProcName)217 MyGetProcAddress (HMODULE hModule, LPCSTR lpProcName)
218 {
219 	/* TODO: Add property for client to set prefix */
220 	const char * prefix ="lib";
221 	char buffer[256];
222 	FARPROC proc = GetProcAddress(hModule, lpProcName);
223 	if (proc)
224 		return proc;
225 	if (lstrlen(lpProcName)+lstrlen(prefix)+1>sizeof(buffer))
226 		return 0;
227 	lstrcpy(buffer, prefix);
228 	lstrcat(buffer, lpProcName);
229 	proc = GetProcAddress(hModule, lpProcName);
230 	if (proc)
231 		return proc;
232 	return 0;
233 }
234 
235 static int
load_dll(void)236 load_dll (void)
237 {
238 	if (f_hinstDll)
239 		return 1;
240 	if (f_failed)
241 		return 0;
242 	f_failed=1;
243 
244 	memset(&f_gettext_fncs, 0, sizeof(f_gettext_fncs));
245 
246 	if (!f_dllpath[0])
247 	{
248 		/* no requested path, try defaults */
249 		int i;
250 		for (i=0; i<sizeof(f_defaults)/sizeof(f_defaults[0]); ++i)
251 		{
252 			if ((f_hinstDll = LoadLibrary(f_defaults[i]))!=NULL)
253 			{
254 				strncpy(f_dllpath, f_defaults[i], sizeof(f_dllpath));
255 				break;
256 			}
257 		}
258 	}
259 	else
260 	{
261 		f_hinstDll = LoadLibrary(f_dllpath);
262 	}
263 	if (!f_hinstDll)
264 		return 0;
265 
266 	f_gettext_fncs.gettext_x = (gettext_type)MyGetProcAddress(f_hinstDll, "gettext");
267 	f_gettext_fncs.dgettext_x = (dgettext_type)MyGetProcAddress(f_hinstDll, "dgettext");
268 	f_gettext_fncs.dcgettext_x = (dcgettext_type)MyGetProcAddress(f_hinstDll, "dcgettext");
269 	f_gettext_fncs.ngettext_x = (ngettext_type)MyGetProcAddress(f_hinstDll, "ngettext");
270 	f_gettext_fncs.dngettext_x = (dngettext_type)MyGetProcAddress(f_hinstDll, "dngettext");
271 	f_gettext_fncs.dcngettext_x = (dcngettext_type)MyGetProcAddress(f_hinstDll, "dcngettext");
272 	f_gettext_fncs.textdomain_x = (textdomain_type)MyGetProcAddress(f_hinstDll, "textdomain");
273 	f_gettext_fncs.bindtextdomain_x = (bindtextdomain_type)MyGetProcAddress(f_hinstDll, "bindtextdomain");
274 	f_gettext_fncs.bind_textdomain_codeset_x = (bind_textdomain_codeset_type)MyGetProcAddress(f_hinstDll, "bind_textdomain_codeset");
275 	f_gettext_fncs.gt_notify_language_change_x = (gt_notify_language_change_type)MyGetProcAddress(f_hinstDll, "gt_notify_language_change");
276 	f_gettext_fncs.gt_get_property_x = (gt_get_property_type)MyGetProcAddress(f_hinstDll, "gt_get_property");
277 	f_gettext_fncs.gt_set_property_x = (gt_set_property_type)MyGetProcAddress(f_hinstDll, "gt_set_property");
278 
279 	f_failed=0;
280 	return 1;
281 }
282 
283 INTLDECL int
intlshim_get_property(const char * name,char * value,int valuelen)284 intlshim_get_property (const char *name, char * value, int valuelen)
285 {
286 	static char file_version[] = "file_version:";
287 	if (!lstrcmp(name, "dll_path"))
288 		return ishim_get_dll_name(value, valuelen);
289 	if (!lstrcmp(name, "dll_version"))
290 	{
291 		char path[_MAX_PATH];
292 		if (!ishim_get_dll_name(path, sizeof(path)))
293 			return 0;
294 		return ishim_get_file_version(path, value, valuelen);
295 	}
296 	if (!lstrcmp(name, "shim_version"))
297 	{
298 		lstrcpyn(value, INTLSHIM_VERSION, valuelen);
299 		return lstrlen(INTLSHIM_VERSION);
300 	}
301 	if (!strncmp(name, file_version, strlen(file_version)))
302 	{
303 		return ishim_get_file_version(name+strlen(file_version), value, valuelen);
304 	}
305 	return 0;
306 }
307 
308 INTLDECL int
intlshim_set_property(const char * name,const char * value)309 intlshim_set_property (const char *name, const char *value)
310 {
311 	if (!strcmp(name, "dll_path"))
312 	{
313 		return ishim_set_dll_name(value);
314 	}
315 	return 0;
316 }
317 
318 
319 static int
ishim_get_dll_name(char * filepath,int pathlen)320 ishim_get_dll_name (char * filepath, int pathlen)
321 {
322 	if (!f_hinstDll)
323 		return 0;
324 	if (!GetModuleFileName(f_hinstDll, filepath, pathlen))
325 		return 0;
326 	return 1;
327 }
328 
329 static int
ishim_get_file_version(const char * filepath,char * verout,int veroutlen)330 ishim_get_file_version (const char * filepath, char * verout, int veroutlen)
331 {
332 	DWORD dwDummyHandle, len;
333 	BYTE * buf = 0;
334 	unsigned int verlen;
335 	LPVOID lpvi;
336 	VS_FIXEDFILEINFO fileInfo;
337 
338 	if (!filepath || !filepath[0]) return 0;
339 
340 	len = GetFileVersionInfoSize((char *)filepath, &dwDummyHandle);
341 	if (!len) return 0;
342 	buf = (BYTE *)malloc(len * sizeof(BYTE));
343 	if (!buf) return 0;
344 	GetFileVersionInfo((char *)filepath, 0, len, buf);
345 	VerQueryValue(buf, "\\", &lpvi, &verlen);
346 	fileInfo = *(VS_FIXEDFILEINFO*)lpvi;
347 	_snprintf(verout, veroutlen, "FV:%d.%d.%d.%d, PV:%d.%d.%d.%d"
348 		, HIWORD(fileInfo.dwFileVersionMS)
349 		, LOWORD(fileInfo.dwFileVersionMS)
350 		, HIWORD(fileInfo.dwFileVersionLS)
351 		, LOWORD(fileInfo.dwFileVersionLS)
352 		, HIWORD(fileInfo.dwProductVersionMS)
353 		, LOWORD(fileInfo.dwProductVersionMS)
354 		, HIWORD(fileInfo.dwProductVersionLS)
355 		, LOWORD(fileInfo.dwProductVersionLS));
356 	free(buf);
357 	return len;
358 }
359 
360 static int
ishim_set_dll_name(const char * filepath)361 ishim_set_dll_name (const char *filepath)
362 {
363 	if (!filepath)
364 		filepath = "";
365 	lstrcpyn(f_dllpath, filepath, sizeof(f_dllpath));
366 
367 	unload_dll();
368 	f_failed=0; /* force attempt to load */
369 	return load_dll();
370 }
371