1/* init.c - Initialize the GnuPG error library. 2 Copyright (C) 2005, 2010 g10 Code GmbH 3 4 This file is part of libgpg-error. 5 6 libgpg-error is free software; you can redistribute it and/or 7 modify it under the terms of the GNU Lesser General Public License 8 as published by the Free Software Foundation; either version 2.1 of 9 the License, or (at your option) any later version. 10 11 libgpg-error is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 Lesser General Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public 17 License along with this program; if not, see <http://www.gnu.org/licenses/>. 18 */ 19 20#if HAVE_CONFIG_H 21#include <config.h> 22#endif 23 24#include <stdlib.h> 25#include <stdio.h> 26#include <string.h> 27#include <errno.h> 28 29#include <gpg-error.h> 30 31#include "gettext.h" 32#include "init.h" 33 34#ifdef HAVE_W32CE_SYSTEM 35# include "mkw32errmap.map.c" /* Generated map_w32codes () */ 36#endif 37 38 39/* Locale directory support. */ 40 41#if HAVE_W32_SYSTEM 42 43#include <windows.h> 44 45static int tls_index = TLS_OUT_OF_INDEXES; /* Index for the TLS functions. */ 46 47static char *get_locale_dir (void); 48static void drop_locale_dir (char *locale_dir); 49 50#else /*!HAVE_W32_SYSTEM*/ 51 52#define get_locale_dir() LOCALEDIR 53#define drop_locale_dir(dir) 54 55#endif /*!HAVE_W32_SYSTEM*/ 56 57 58static void 59real_init (void) 60{ 61#ifdef ENABLE_NLS 62 char *locale_dir; 63 64 /* We only have to bind our locale directory to our text domain. */ 65 locale_dir = get_locale_dir (); 66 if (locale_dir) 67 { 68 bindtextdomain (PACKAGE, locale_dir); 69 drop_locale_dir (locale_dir); 70 } 71#endif 72} 73 74/* Initialize the library. This function should be run early. */ 75gpg_error_t 76gpg_err_init (void) 77{ 78#ifdef HAVE_W32_SYSTEM 79# ifdef DLL_EXPORT 80 /* We always have a constructor and thus this function is called 81 automatically. Due to the way the C init code of mingw works, 82 the constructors are called before our DllMain function is 83 called. The problem with that is that the TLS has not been setup 84 and w32-gettext.c requires TLS. To solve this we do nothing here 85 but call the actual init code from our DllMain. */ 86# else /*!DLL_EXPORT*/ 87 /* Note that if the TLS is actually used, we can't release the TLS 88 as there is no way to know when a thread terminates (i.e. no 89 thread-specific-atexit). You are really better off to use the 90 DLL! */ 91 if (tls_index == TLS_OUT_OF_INDEXES) 92 { 93 tls_index = TlsAlloc (); 94 if (tls_index == TLS_OUT_OF_INDEXES) 95 { 96 /* No way to continue - commit suicide. */ 97 abort (); 98 } 99 _gpg_w32__init_gettext_module (); 100 real_init (); 101 } 102# endif /*!DLL_EXPORT*/ 103#else 104 real_init (); 105#endif 106 return 0; 107} 108 109 110/* Deinitialize libgpg-error. This function is only used in special 111 circumstances. No gpg-error function should be used after this 112 function has been called. A value of 0 passed for MODE 113 deinitializes the entire libgpg-error, a value of 1 releases 114 resources allocated for the current thread and only that thread may 115 not anymore access libgpg-error after such a call. Under Windows 116 this function may be called from the DllMain function of a DLL 117 which statically links to libgpg-error. */ 118void 119gpg_err_deinit (int mode) 120{ 121#if defined (HAVE_W32_SYSTEM) && !defined(DLL_EXPORT) 122 struct tls_space_s *tls; 123 124 tls = TlsGetValue (tls_index); 125 if (tls) 126 { 127 TlsSetValue (tls_index, NULL); 128 LocalFree (tls); 129 } 130 131 if (mode == 0) 132 { 133 TlsFree (tls_index); 134 tls_index = TLS_OUT_OF_INDEXES; 135 } 136#else 137 (void)mode; 138#endif 139} 140 141 142 143#ifdef HAVE_W32_SYSTEM 144 145/* Return a malloced string encoded in UTF-8 from the wide char input 146 string STRING. Caller must free this value. Returns NULL on 147 failure. Caller may use GetLastError to get the actual error 148 number. The result of calling this function with STRING set to 149 NULL is not defined. */ 150static char * 151wchar_to_utf8 (const wchar_t *string) 152{ 153 int n; 154 char *result; 155 156 /* Note, that CP_UTF8 is not defined in Windows versions earlier 157 than NT. */ 158 n = WideCharToMultiByte (CP_UTF8, 0, string, -1, NULL, 0, NULL, NULL); 159 if (n < 0) 160 return NULL; 161 162 result = malloc (n+1); 163 if (result) 164 { 165 n = WideCharToMultiByte (CP_UTF8, 0, string, -1, result, n, NULL, NULL); 166 if (n < 0) 167 { 168 free (result); 169 result = NULL; 170 } 171 } 172 return result; 173} 174 175 176/* Return a malloced wide char string from an UTF-8 encoded input 177 string STRING. Caller must free this value. Returns NULL on 178 failure. Caller may use GetLastError to get the actual error 179 number. The result of calling this function with STRING set to 180 NULL is not defined. */ 181static wchar_t * 182utf8_to_wchar (const char *string) 183{ 184 int n; 185 wchar_t *result; 186 187 n = MultiByteToWideChar (CP_UTF8, 0, string, -1, NULL, 0); 188 if (n < 0) 189 return NULL; 190 191 result = malloc ((n+1) * sizeof *result); 192 if (result) 193 { 194 n = MultiByteToWideChar (CP_UTF8, 0, string, -1, result, n); 195 if (n < 0) 196 { 197 free (result); 198 result = NULL; 199 } 200 return NULL; 201 } 202 return result; 203} 204 205 206static char * 207get_locale_dir (void) 208{ 209 static wchar_t moddir[MAX_PATH+5]; 210 char *result, *p; 211 int nbytes; 212 213 if (!GetModuleFileNameW (NULL, moddir, MAX_PATH)) 214 *moddir = 0; 215 216#define SLDIR "\\share\\locale" 217 if (*moddir) 218 { 219 nbytes = WideCharToMultiByte (CP_UTF8, 0, moddir, -1, NULL, 0, NULL, NULL); 220 if (nbytes < 0) 221 return NULL; 222 223 result = malloc (nbytes + strlen (SLDIR) + 1); 224 if (result) 225 { 226 nbytes = WideCharToMultiByte (CP_UTF8, 0, moddir, -1, 227 result, nbytes, NULL, NULL); 228 if (nbytes < 0) 229 { 230 free (result); 231 result = NULL; 232 } 233 else 234 { 235 p = strrchr (result, '\\'); 236 if (p) 237 *p = 0; 238 /* If we are installed below "bin" strip that part and 239 use the top directory instead. 240 241 Background: Under Windows we don't install GnuPG 242 below bin/ but in the top directory with only share/, 243 lib/, and etc/ below it. One of the reasons is to 244 keep the the length of the filenames at bay so not to 245 increase the limited length of the PATH envvar. 246 Another and more important reason, however, is that 247 the very first GPG versions on W32 were installed 248 into a flat directory structure and for best 249 compatibility with these versions we didn't changed 250 that later. For WindowsCE we can right away install 251 it under bin, though. The hack with detection of the 252 bin directory part allows us to eventually migrate to 253 such a directory layout under plain Windows without 254 the need to change libgpg-error. */ 255 p = strrchr (result, '\\'); 256 if (p && !strcmp (p+1, "bin")) 257 *p = 0; 258 /* Append the static part. */ 259 strcat (result, SLDIR); 260 } 261 } 262 } 263 else /* Use the old default value. */ 264 { 265 result = malloc (10 + strlen (SLDIR) + 1); 266 if (result) 267 { 268 strcpy (result, "c:\\gnupg"); 269 strcat (result, SLDIR); 270 } 271 } 272#undef SLDIR 273 return result; 274} 275 276 277static void 278drop_locale_dir (char *locale_dir) 279{ 280 free (locale_dir); 281} 282 283 284/* Return the tls object. This function is guaranteed to return a 285 valid non-NULL object. */ 286struct tls_space_s * 287get_tls (void) 288{ 289 struct tls_space_s *tls; 290 291 tls = TlsGetValue (tls_index); 292 if (!tls) 293 { 294 /* Called by a thread which existed before this DLL was loaded. 295 Allocate the space. */ 296 tls = LocalAlloc (LPTR, sizeof *tls); 297 if (!tls) 298 { 299 /* No way to continue - commit suicide. */ 300 abort (); 301 } 302 tls->gt_use_utf8 = 0; 303 TlsSetValue (tls_index, tls); 304 } 305 306 return tls; 307} 308 309 310/* Return the value of the ERRNO variable. This needs to be a 311 function so that we can have a per-thread ERRNO. This is used only 312 on WindowsCE because that OS misses an errno. */ 313#ifdef HAVE_W32CE_SYSTEM 314int 315_gpg_w32ce_get_errno (void) 316{ 317 return map_w32codes ( GetLastError () ); 318} 319#endif /*HAVE_W32CE_SYSTEM*/ 320 321 322/* Replacement strerror function for WindowsCE. */ 323#ifdef HAVE_W32CE_SYSTEM 324char * 325_gpg_w32ce_strerror (int err) 326{ 327 struct tls_space_s *tls = get_tls (); 328 wchar_t tmpbuf[STRBUFFER_SIZE]; 329 int n; 330 331 if (err == -1) 332 err = _gpg_w32ce_get_errno (); 333 334 /* Note: On a German HTC Touch Pro2 device I also tried 335 LOCALE_USER_DEFAULT and LOCALE_SYSTEM_DEFAULT - both returned 336 English messages. */ 337 if (FormatMessageW (FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 338 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), 339 tmpbuf, STRBUFFER_SIZE -1, 340 NULL)) 341 { 342 n = WideCharToMultiByte (CP_UTF8, 0, tmpbuf, -1, 343 tls->strerror_buffer, 344 sizeof tls->strerror_buffer -1, 345 NULL, NULL); 346 } 347 else 348 n = -1; 349 350 if (n < 0) 351 snprintf (tls->strerror_buffer, sizeof tls->strerror_buffer -1, 352 "[w32err=%d]", err); 353 return tls->strerror_buffer; 354} 355#endif /*HAVE_W32CE_SYSTEM*/ 356 357 358void 359gpg_err_set_errno (int err) 360{ 361#ifdef HAVE_W32CE_SYSTEM 362 SetLastError (err); 363#else /*!HAVE_W32CE_SYSTEM*/ 364 errno = err; 365#endif /*!HAVE_W32CE_SYSTEM*/ 366} 367 368 369/* Entry point called by the DLL loader. */ 370#ifdef DLL_EXPORT 371int WINAPI 372DllMain (HINSTANCE hinst, DWORD reason, LPVOID reserved) 373{ 374 struct tls_space_s *tls; 375 (void)reserved; 376 377 switch (reason) 378 { 379 case DLL_PROCESS_ATTACH: 380 tls_index = TlsAlloc (); 381 if (tls_index == TLS_OUT_OF_INDEXES) 382 return FALSE; 383 /* falltru. */ 384 case DLL_THREAD_ATTACH: 385 tls = LocalAlloc (LPTR, sizeof *tls); 386 if (!tls) 387 return FALSE; 388 tls->gt_use_utf8 = 0; 389 TlsSetValue (tls_index, tls); 390 if (reason == DLL_PROCESS_ATTACH) 391 { 392 real_init (); 393 } 394 break; 395 396 case DLL_THREAD_DETACH: 397 tls = TlsGetValue (tls_index); 398 if (tls) 399 LocalFree (tls); 400 break; 401 402 case DLL_PROCESS_DETACH: 403 tls = TlsGetValue (tls_index); 404 if (tls) 405 LocalFree (tls); 406 TlsFree (tls_index); 407 break; 408 409 default: 410 break; 411 } 412 413 return TRUE; 414} 415#endif /*DLL_EXPORT*/ 416 417#else /*!HAVE_W32_SYSTEM*/ 418 419void 420gpg_err_set_errno (int err) 421{ 422 errno = err; 423} 424 425#endif /*!HAVE_W32_SYSTEM*/ 426