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